package cn.com.cifi.mars.service.impl;

import cn.com.cifi.mars.bean.bo.*;
import cn.com.cifi.mars.bean.dto.*;
import cn.com.cifi.mars.bean.vo.price.FilePreviewVO;
import cn.com.cifi.mars.bean.vo.signPlan.*;
import cn.com.cifi.mars.bean.vo.signPlan.req.SignPlanDetailParam;
import cn.com.cifi.mars.bean.vo.signPlan.req.SignPlanOaParam;
import cn.com.cifi.mars.bean.vo.signPlan.signOverall.MonthSignVo;
import cn.com.cifi.mars.bean.vo.signPlan.signOverall.OverallSignPlanVo;
import cn.com.cifi.mars.bean.vo.signPlan.signOverall.SupplyAndSignVo;
import cn.com.cifi.mars.bean.vo.signPlan.signOverall.YearSignVo;
import cn.com.cifi.mars.common.exception.MsgException;
import cn.com.cifi.mars.common.redis.RedisTemplateUtil;
import cn.com.cifi.mars.constant.*;
import cn.com.cifi.mars.entity.*;
import cn.com.cifi.mars.entity.common.IdmUser;
import cn.com.cifi.mars.entity.common.ProjectByStages;
import cn.com.cifi.mars.entity.common.SysDictionary;
import cn.com.cifi.mars.entity.common.SysProjectFetchNumber;
import cn.com.cifi.mars.mapper.mars.*;
import cn.com.cifi.mars.service.*;
import cn.com.cifi.mars.util.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

/**
 * <p>
 * 签约计划版本表 服务实现类
 * </p>
 *
 * @author yyd
 * @since 2019-11-08
 */
@Slf4j
@Service
public class SignPlanVersionServiceImpl extends ServiceImpl<SignPlanVersionMapper, SignPlanVersion>
		implements SignPlanVersionService {

    @Autowired
    private SysDictionaryService sysDictionaryService;
    @Autowired
    private SupplyPlanVersionMapper supplyPlanVersionMapper;
    @Autowired
    private SignPlanVersionMapper signPlanVersionMapper;
    @Autowired
    private ProjectByStagesMapper projectByStagesMapper;
    @Autowired
    private SignPlanVersionDetailMapper signPlanVersionDetailMapper;
    @Autowired
    private SignPlanVersionSupplyDetailMapper signPlanVersionSupplyDetailMapper;
    @Autowired
    private SignPlanVersionSupplyDetailService signPlanVersionSupplyDetailService;
    @Autowired
    private SignPlanVersionDetailCopyService signPlanVersionDetailCopyService;
    @Autowired
    private MonthTradePlanMapper monthTradePlanMapper;
    @Autowired
    private ProjectBasicService projectBasicService;
    @Autowired
    private SubmitApprovedProjectService submitApprovedProjectService;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private ApprovalBillService approvalBillService;
    @Autowired
    private FileUploadService fileUploadService;
    @Autowired
    private ApprovalBillMapper approvalBillMapper;
    @Autowired
    private ApprovedProjectMapper approvedProjectMapper;
    @Autowired
    private ApprovalBillDetailMapper approvalBillDetailMapper;
    @Autowired
    private FileUploadMapper fileUploadMapper;
    @Autowired
    private SignPlanApprovedMapper signPlanApprovedMapper;
    @Autowired
    private SignPlanEditionMapper signPlanEditionMapper;
    @Autowired
    private SysProjectFetchNumberService sysProjectFetchNumberService;
    @Autowired
    private DmOdsMyVsMarsSContractService dmOdsMyVsMarsSContractService;
    @Autowired
    private DmOdsNosTradedataviewformarsService dmOdsNosTradedataviewformarsService;
    @Autowired
    private IdmUserService idmUserService;
    @Autowired
    private SysLogService sysLogService;
    @Autowired
    private StageService stageService;
    @Autowired
    private StagePartNersService stagePartNersService;
    @Autowired
    private ProjectMapper projectMapper;
    @Autowired
    private CommonService commonService;
    @Autowired
    private PriceVersionService priceVersionService;
    @Autowired
    private PriceVersionMapper priceVersionMapper;
    @Autowired
    private PriceValueOrgMapper priceValueOrgMapper;
    @Autowired
    private PortalTodoMapper portalTodoMapper;
    @Autowired
    private RedisTemplateUtil redisTemplateUtil;
    @Autowired
    private VersionLogMapper versionLogMapper;
    @Autowired
    private DmDmPlanSignMonthService dmDmPlanSignMonthService;
    @Autowired
    private MymRoomsService mymRoomsService;
    
    @Autowired
	private BudgetLockRecordService budgetLockRecordService;

    @Value("${sign_notice_pc_url}")
    private String signNoticePcUrl;

    @Value("${mobile_task_url}")
    private String signNoticeAppUrl;

    @Value("${previewUrl}")
    private String url;

    @Value("${view_approved_url}")
    private String reviewApprovalUrl;

    @Value("${PORTAL_SYSCODE}")
    private String oaSysCode;

    @Value("${PORTAL_SECRET_KEY}")
    private String oaSecretKey;

    /**
     * 创建签约计划版本
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized String createSignPlan(String projectFid, String versionType, String versionIdentify,String alias) {
        // 创建签约计划版本通用规则：
        // 验证是否可以创建签约计划
        isCreateSigPlan(projectFid, versionType);

        // 投资版
        if (versionType.equals(VersionTypeEunm.INVESTMENT.getKey())) {
            return createCastDecision(projectFid,versionIdentify, alias);
        // 战规版
        } else if (versionType.equals(VersionTypeEunm.WARPLAN.getKey())) {
            return createWarplan(projectFid,versionIdentify, alias);
        // 动态版
        } else if (versionType.equals(VersionTypeEunm.DYNAMIC.getKey())) {
            return createDynamic(projectFid,versionIdentify, alias);
        // 预算版
        } else if (versionType.equals(VersionTypeEunm.BUDGET.getKey())) {
            return createBudget(projectFid, alias);
        }
        throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
    }

    /**
     * 异常创建签约计划版本
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String exceptionCreate(String projectFid, String versionType,String versionIdentify,String alias) {
        // 创建签约计划版本通用规则：
        // 验证是否可以创建签约计划
        isCreateSigPlan(projectFid, versionType);
        // 投资版
        if (versionType.equals(VersionTypeEunm.INVESTMENT.getKey())) {
            return exceptionCreateCastDecision(projectFid,versionIdentify, alias);
            // 战规版
        } else if (versionType.equals(VersionTypeEunm.WARPLAN.getKey())) {
            return exceptionCreateWarplan(projectFid,versionIdentify, alias);
            // 动态版
        } else if (versionType.equals(VersionTypeEunm.DYNAMIC.getKey())) {
            return exceptionCreateDynamic(projectFid, versionIdentify,alias);
        }
        throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
    }

    /**
     * 创建投资版签约计划
     */
    @Transactional(rollbackFor = Exception.class)
    public String createCastDecision(String projectFid,String versionIdentify, String alias) {
        List<String> versionTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey());
        List<String> versionStatuss = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());


        // 投资版签约计划创建规则：
        // 1、分期不存在投资版供货计划时，不允许新建该分期投资版签约计划版本
        SupplyPlanVersion spv = signPlanVersionMapper.selectSupplyPlanByProjectFid(projectFid, versionTypes, IsDeleteEnum.NO.getKey());
        if (null == spv) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
        }
        //被复制的签约计划
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());;

        // 2、分期战规后不允许普通用户新建该分期投资版签约计划版本；此时可通过异常报事机制，由系统管理员新建投资版签约计划版本后，业务人员编制投资版签约计划；
        if (isAfterWarplan(spv.getProjectfId())) {
        // 3、如果分期下没有投资版签约计划，不受规则“2”限制，可直接新建投资版签约计划；
            if(oldSignPlanVersion != null){
                throw new MsgException(CodeEnum.EXIST_AFTER_WARPLAN_VERSIONS);
            }
        }

        // 签约版本初始化
        String newSignPlanId = insertVersion(spv,versionIdentify, alias, VersionTypeEunm.INVESTMENT.getKey(),VersionCreateEnum.MANUALCREATE.getKey());
        // 签约计划供货详情表初始化
        initializeCastDecisionSupplyDetail(newSignPlanId, spv, alias);

        //签约计划详情初始化所需参数
        SignDetailInitializeBO signDetailInitializeBO = new SignDetailInitializeBO();
        signDetailInitializeBO.setVersionId(newSignPlanId);
        signDetailInitializeBO.setVersionType(VersionTypeEunm.INVESTMENT.getKey());
        signDetailInitializeBO.setProjectFid(projectFid);
        signDetailInitializeBO.setVersionAttr(VersionAttrEnum.BEFORE_WARPLAN.getKey());
        signDetailInitializeBO.setOldSignPlanVersion(oldSignPlanVersion);
        signDetailInitializeBO.setAlias(alias);
        // 签约计划详情表初始化
        initializeDetail(signDetailInitializeBO);
        return newSignPlanId;
    }

    /**
     * 创建战规版签约计划
     */
    @Transactional(rollbackFor = Exception.class)
    public String createWarplan(String projectFid,String versionIdentify, String alias) {
        List<String> versionTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey());
        List<String> versionStatuss = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());

        // 战规版签约计划创建规则：
        // 1、分期不存在战规版供货计划时，不允许新建该分期战规版签约计划版本
        SupplyPlanVersion spv = signPlanVersionMapper.selectSupplyPlanByProjectFid(projectFid, versionTypes, IsDeleteEnum.NO.getKey());
        if (null == spv) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
        }
        // 2、分期大定价后不允许普通用户新建该分期战规版签约计划版本；此时可通过异常报事机制，由系统管理员新建战规版签约计划版本后，业务人员编制战规版签约计划；
        // 3、如果分期下没有战规版签约计划，不受规则“2”限制，可直接新建战规版签约计划；
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());
        if (null != signPlanVersion) {
            versionTypes = Arrays.asList(VersionTypeEunm.GETPRICE.getKey());
            List<PriceVersion> priceVersionList = signPlanVersionMapper.selectPriceByVersionTypes(projectFid, versionTypes, IsDeleteEnum.NO.getKey());
            if (CollectionUtils.isNotEmpty(priceVersionList)) {
                throw new MsgException(CodeEnum.EXIST_APPROVAL_WARPLAN_VERSIONS);
            }
        }
        //分期进入一房一价后,需要存在审批通过动态版或预算版签约计划才可以创建战规版签约计划
        boolean isCreate = checkWarplanCreate(projectFid);
        if(!isCreate){
            throw new MsgException(CodeEnum.CREATE_WARPLAN_ERROR);
        }
        // 复制分期战规后最新审批通过签约计划版本（月度动态版、预算版、战规版）中相同产品构成的签约计划数据（直接复制，不进行倒挤，手动调整）
        versionTypes = Arrays.asList(VersionTypeEunm.DYNAMIC.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.WARPLAN.getKey());
        //被复制的签约计划
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());

        // 签约版本初始化
        String newSignPlanId = insertVersion(spv,versionIdentify, alias, VersionTypeEunm.WARPLAN.getKey(),VersionCreateEnum.MANUALCREATE.getKey());
        // 签约计划供货详情表初始化
        initializeSignPlan(newSignPlanId, spv, alias);
        //签约计划详情初始化所需参数
        SignDetailInitializeBO signDetailInitializeBO = new SignDetailInitializeBO();
        signDetailInitializeBO.setVersionId(newSignPlanId);
        signDetailInitializeBO.setVersionType(VersionTypeEunm.WARPLAN.getKey());
        signDetailInitializeBO.setProjectFid(projectFid);
        signDetailInitializeBO.setVersionAttr(VersionAttrEnum.AFTER_WARPLAN.getKey());
        signDetailInitializeBO.setOldSignPlanVersion(oldSignPlanVersion);
        signDetailInitializeBO.setAlias(alias);
        // 签约计划详情表初始化
        initializeDetail(signDetailInitializeBO);
        return newSignPlanId;

    }

    /**
     * 分期进入一房一价后,需要存在审批通过动态版或预算版签约计划才可以创建战规版签约计划
     * @param projectfId
     * @return
     */
    private boolean checkWarplanCreate(String projectfId) {
        boolean isContainSale = false;
        //1、分期下历史中是否进入过一房一价
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectContainSale(projectfId,null,VersionStatusEnum.SUCCESSAPPROVED.getKey());
        if(null != signPlanVersion){
            isContainSale = true;
        }else {
            //2、当前是否进入一房一价
            List<MymRooms> rooms = mymRoomsService.selectNewestWarPlanByFid(projectfId);
            if(CollectionUtils.isNotEmpty(rooms)){
                isContainSale = true;
            }
        }
        //3、是否存在动态版或预算版签约计划
        if(isContainSale){
            List<String> versionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
            List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
            //分期一房一价后
            signPlanVersion = signPlanVersionMapper.selectNewesVersion(projectfId, afterSaleTypes, versionStates, IsDeleteEnum.NO.getKey());
            if(null == signPlanVersion){
                return false;
            }
        }
        return true;
    }

    /**
     * 创建预算版签约计划
     */
    @Transactional(rollbackFor = Exception.class)
    public String createBudget(String projectFid, String alias) {
        // 1、当前日期没有到在供销存系统配置的全年（或年中）预算编制期；
        // 版本标识（预算版：mid_year--年中；all_year--全年）
        String versionIdentify = "";
        // 预算版的编制年份
        String compileYear = "";
        LocalDateTime now = LocalDateTime.now();

        /**
         * 5-8月 当年年中 10-12 当年全年 1-3 月 下一年全年
         */
        SysDictionary dictionary = sysDictionaryService.selectDictionaryByTypeCode(Constants.BUDGET_CYCLE_TYPE_CODE,now.getMonthValue()+"");
        if(dictionary == null) {
            throw new MsgException(CodeEnum.CANNOT_CREATE_VERSION);
        }else {
            String budgetCycleCode = dictionary.getDictCode();
            if (BudgetCycleEnum.ALL_YEAR_BUDGET.getKey().equals(budgetCycleCode)) {
                versionIdentify = VersionIdentifyEnum.ALL_YEAR.getKey();
                compileYear = now.getYear() + "";
            }
            if (BudgetCycleEnum.MID_YEAR_BUDGET.getKey().equals(budgetCycleCode)) {
                versionIdentify = VersionIdentifyEnum.MID_YEAR.getKey();
                compileYear = now.getYear() + "";
            }
            if (BudgetCycleEnum.NEXT_ALL_YEAR_BUDGET.getKey().equals(budgetCycleCode)) {
                versionIdentify = VersionIdentifyEnum.ALL_YEAR.getKey();
                compileYear = now.getYear() + 1 + "";
            }
        }

        List<String> versionTypes = new ArrayList<>();
        List<String> versionStatuss = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());

        //版本阶段
        String versionAttr;
        //基于的预算版供货版本
        SupplyPlanVersion spv;
        boolean afterWarplan = isAfterWarplan(projectFid);
        // 2、不存在当年全年（或年中）当前版本阶段预算版供货计划，不能创建当年全年（或年中）预算版签约计划；此时需先创建当年全年（或年中）预算版供货计划；
        if (afterWarplan) {
            versionAttr = VersionAttrEnum.AFTER_WARPLAN.getKey();
            versionTypes.add(VersionTypeEunm.DYNAMIC.getKey());
            versionTypes.add(VersionTypeEunm.WARPLAN.getKey());
            versionTypes.add(VersionTypeEunm.BUDGET.getKey());
        } else {
            versionAttr = VersionAttrEnum.BEFORE_WARPLAN.getKey();
            versionTypes.add(VersionTypeEunm.INVESTMENT.getKey());
            versionTypes.add(VersionTypeEunm.BUDGET.getKey());
        }
        spv = signPlanVersionMapper.selectBudgetSupplyPlan(projectFid, VersionTypeEunm.BUDGET.getKey(), versionIdentify,compileYear,versionAttr, IsDeleteEnum.NO.getKey());
        if(null == spv){
            throw new MsgException(CodeEnum.NOT_THIS_YEAR_SUPPLY_PLAN_VERSION);
        }
        // 复制最新审批通过签约计划版本中相同产品构成的签约计划数据（直接复制，不进行倒挤，手动调整）
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());

        // 初始化预算版签约计划版本
        String newSignPlanId = insertBudgetSignPlanVersion(spv, versionAttr,versionIdentify, compileYear, alias);
        // 签约计划供货详情表初始化
        if(VersionAttrEnum.BEFORE_WARPLAN.getKey().equals(versionAttr)){
            initializeCastDecisionSupplyDetail(newSignPlanId, spv, alias);
        }else {
            initializeSignPlan(newSignPlanId, spv, alias);
        }
        //签约计划详情初始化所需参数
        SignDetailInitializeBO signDetailInitializeBO = new SignDetailInitializeBO();
        signDetailInitializeBO.setVersionId(newSignPlanId);
        signDetailInitializeBO.setVersionType(VersionTypeEunm.BUDGET.getKey());
        signDetailInitializeBO.setProjectFid(projectFid);
        signDetailInitializeBO.setVersionAttr(versionAttr);
        signDetailInitializeBO.setOldSignPlanVersion(oldSignPlanVersion);
        signDetailInitializeBO.setAlias(alias);
        // 签约计划详情表初始化
        initializeDetail(signDetailInitializeBO);
        return newSignPlanId;
    }

    /**
     * 创建动态版签约计划
     */

    @Transactional(rollbackFor = Exception.class)
    public String createDynamic(String projectFid, String versionIdentify,String alias) {
        List<String> versionTypes = new ArrayList<>();
        versionTypes.add(VersionTypeEunm.DYNAMIC.getKey());
        versionTypes.add(VersionTypeEunm.WARPLAN.getKey());
        List<String> versionStatuss = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        /**
          动态版签约计划创建对供货计划的取数优先级
         1、上月确认供货的（月度动态版）
         2、当月之前确认供货或最新定稿封版的（月度动态版）
         3、当前最新版（月度动态版、战规版）
         **/
        //查询上个月(yyyy-MM)
        String lastYearMonth;
        //若当前时间处于一号0点到6点之间，则取上上个月供货计划
        if (LocalDateTimeUtils.isBetween0And6()) {
            lastYearMonth = LocalDateTimeUtils.getLocalMonth(LocalDateTime.now().minusMonths(2L));
        } else {
            lastYearMonth = LocalDateTimeUtils.getLocalMonth(LocalDateTime.now().minusMonths(1L));
        }
        //确认与供货条件
        ConfirmAndFuinalizeBO confirmAndFuinalizeBO =new ConfirmAndFuinalizeBO();
        confirmAndFuinalizeBO.setProjectfId(projectFid);
        confirmAndFuinalizeBO.setVersionType(VersionTypeEunm.DYNAMIC.getKey());
        confirmAndFuinalizeBO.setAffiliationMonth(lastYearMonth);
        confirmAndFuinalizeBO.setConfirmStatus(1);
        confirmAndFuinalizeBO.setIsFinalize(1);
        confirmAndFuinalizeBO.setIsDel(IsDeleteEnum.NO.getKey());

        //查询分期下指定月份最新确认供货的供货计划版本
        SupplyPlanVersion spv = signPlanVersionMapper.selectConfirmStatusSupplyPlan(confirmAndFuinalizeBO);
        if (null == spv) {
            //查询分期下指定月份之前最新确认或定稿的供货计划版本
            spv = signPlanVersionMapper.selectSupplyPlanByAffiliationMonth(confirmAndFuinalizeBO);
        }
        if (null == spv) {
            //查询分期下当前最新版（月度动态版、战规版）
            spv = signPlanVersionMapper.selectSupplyPlanByProjectFid(projectFid, versionTypes, IsDeleteEnum.NO.getKey());
            if(null == spv){
                saveLog(LogEventEnum.ADD,"签约计划","签约计划创建失败","分期id:"+projectFid +"无战规后的供货计划版本");
                throw new MsgException(CodeEnum.NOT_CONFIRM__FINALIZE_SUPPLY_PLAN_VERSION_ERROR);
            }
        }

        // 复制分期战规后最新审批通过签约计划版本（月度动态版、战规版）中相同产品构成的签约计划数据（直接复制，不进行倒挤，手动调整）
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());

        // 签约版本初始化
        String newSignPlanId = insertVersion(spv,versionIdentify, alias, VersionTypeEunm.DYNAMIC.getKey(),VersionCreateEnum.MANUALCREATE.getKey());
        // 签约计划供货详情表初始化
        initializeSignPlan(newSignPlanId, spv, alias);
        //签约计划详情初始化所需参数
        SignDetailInitializeBO signDetailInitializeBO = new SignDetailInitializeBO();
        signDetailInitializeBO.setVersionId(newSignPlanId);
        signDetailInitializeBO.setVersionType(VersionTypeEunm.DYNAMIC.getKey());
        signDetailInitializeBO.setProjectFid(projectFid);
        signDetailInitializeBO.setVersionAttr(VersionAttrEnum.AFTER_WARPLAN.getKey());
        signDetailInitializeBO.setOldSignPlanVersion(oldSignPlanVersion);
        signDetailInitializeBO.setAlias(alias);
        // 签约计划详情表初始化
        initializeDetail(signDetailInitializeBO);
        return newSignPlanId;
    }

    /**
     * 异常创建投资版签约计划
     */
    @Transactional(rollbackFor = Exception.class)
    private String exceptionCreateCastDecision(String projectFid, String versionIdentify,String alias) {
        List<String> versionTypes = new ArrayList<>();
        versionTypes.add(VersionTypeEunm.INVESTMENT.getKey());
        List<String> versionStatuss = new ArrayList<>();
        versionStatuss.add(VersionStatusEnum.SUCCESSAPPROVED.getKey());

        // 分期不存在投资版供货计划时，不允许新建该分期投资版签约计划版本
        SupplyPlanVersion spv = signPlanVersionMapper.selectSupplyPlanByProjectFid(projectFid, versionTypes, IsDeleteEnum.NO.getKey());
        if (null == spv) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
        }
        //查询最新审批通过投资版签约计划
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());

        // 签约版本初始化
        String newSignPlanId = insertVersion(spv,versionIdentify, alias, VersionTypeEunm.INVESTMENT.getKey(),VersionCreateEnum.EXCEPTIONCREATE.getKey());
        // 签约计划供货详情表初始化
        initializeCastDecisionSupplyDetail(newSignPlanId, spv, alias);
        //签约计划详情初始化所需参数
        SignDetailInitializeBO signDetailInitializeBO = new SignDetailInitializeBO();
        signDetailInitializeBO.setVersionId(newSignPlanId);
        signDetailInitializeBO.setVersionType(VersionTypeEunm.INVESTMENT.getKey());
        signDetailInitializeBO.setProjectFid(projectFid);
        signDetailInitializeBO.setVersionAttr(VersionAttrEnum.BEFORE_WARPLAN.getKey());
        signDetailInitializeBO.setOldSignPlanVersion(oldSignPlanVersion);
        signDetailInitializeBO.setAlias(alias);
        // 签约计划详情表初始化
        initializeDetail(signDetailInitializeBO);
        return newSignPlanId;
    }

    /**
     * 异常创建战规版签约计划
     */
    @Transactional(rollbackFor = Exception.class)
    public String exceptionCreateWarplan(String projectFid,String versionIdentify, String alias) {
        List<String> versionTypes = new ArrayList<>();

        versionTypes.add(VersionTypeEunm.WARPLAN.getKey());
        List<String> versionStatuss = new ArrayList<>();
        versionStatuss.add(VersionStatusEnum.SUCCESSAPPROVED.getKey());

        //分期不存在战规版供货计划时，不允许新建该分期战规版签约计划版本
        SupplyPlanVersion spv = signPlanVersionMapper.selectSupplyPlanByProjectFid(projectFid, versionTypes, IsDeleteEnum.NO.getKey());
        if (null == spv) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
        }

        versionTypes.add(VersionTypeEunm.DYNAMIC.getKey());
        versionTypes.add(VersionTypeEunm.BUDGET.getKey());
        //复制分期战规后最新审批通过签约计划版本（月度动态版、预算版、战规版）中相同产品构成的签约计划数据（直接复制，不进行倒挤，手动调整）
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes,
                versionStatuss, IsDeleteEnum.NO.getKey());
        //分期进入一房一价后,需要存在审批通过动态版或预算版签约计划才可以创建战规版签约计划
        boolean isCreate = checkWarplanCreate(projectFid);
        if(!isCreate){
            throw new MsgException(CodeEnum.CREATE_WARPLAN_ERROR);
        }

        // 签约版本初始化
        String newSignPlanId = insertVersion(spv,versionIdentify, alias, VersionTypeEunm.WARPLAN.getKey(),VersionCreateEnum.EXCEPTIONCREATE.getKey());
        // 签约计划供货详情表初始化
        initializeSignPlan(newSignPlanId, spv, alias);
        //签约计划详情初始化所需参数
        SignDetailInitializeBO signDetailInitializeBO = new SignDetailInitializeBO();
        signDetailInitializeBO.setVersionId(newSignPlanId);
        signDetailInitializeBO.setVersionType(VersionTypeEunm.WARPLAN.getKey());
        signDetailInitializeBO.setProjectFid(projectFid);
        signDetailInitializeBO.setVersionAttr(VersionAttrEnum.AFTER_WARPLAN.getKey());
        signDetailInitializeBO.setOldSignPlanVersion(oldSignPlanVersion);
        signDetailInitializeBO.setAlias(alias);
        // 签约计划详情表初始化
        initializeDetail(signDetailInitializeBO);
        return newSignPlanId;

    }

    /**
     * 异常创建动态版签约计划
     */
    @Transactional(rollbackFor = Exception.class)
    public String exceptionCreateDynamic(String projectFid,String versionIdentify, String alias) {
        List<String> versionTypes = new ArrayList<>();
        versionTypes.add(VersionTypeEunm.DYNAMIC.getKey());
        versionTypes.add(VersionTypeEunm.WARPLAN.getKey());
//        versionTypes.add(VersionTypeEunm.BUDGET.getKey());
        List<String> versionStatuss = new ArrayList<>();
        versionStatuss.add(VersionStatusEnum.SUCCESSAPPROVED.getKey());

        //查询分期下当前最新版（月度动态版、战规版）
        SupplyPlanVersion spv = signPlanVersionMapper.selectSupplyPlanByProjectFid(projectFid, versionTypes, IsDeleteEnum.NO.getKey());
        if (null == spv) {
            throw new MsgException(CodeEnum.NOT_CONFIRM__FINALIZE_SUPPLY_PLAN_VERSION_ERROR);
        }

        // 复制分期战规后最新审批通过签约计划版本（月度动态版、战规版）中相同产品构成的签约计划数据（直接复制，不进行倒挤，手动调整）
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectFid, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());

        // 签约版本初始化
        String newSignPlanId = insertVersion(spv,versionIdentify, alias, VersionTypeEunm.DYNAMIC.getKey(),VersionCreateEnum.EXCEPTIONCREATE.getKey());
        // 签约计划供货详情表初始化
        initializeSignPlan(newSignPlanId, spv, alias);
        //签约计划详情初始化所需参数
        SignDetailInitializeBO signDetailInitializeBO = new SignDetailInitializeBO();
        signDetailInitializeBO.setVersionId(newSignPlanId);
        signDetailInitializeBO.setVersionType(VersionTypeEunm.DYNAMIC.getKey());
        signDetailInitializeBO.setProjectFid(projectFid);
        signDetailInitializeBO.setVersionAttr(VersionAttrEnum.AFTER_WARPLAN.getKey());
        signDetailInitializeBO.setOldSignPlanVersion(oldSignPlanVersion);
        signDetailInitializeBO.setAlias(alias);
        // 签约计划详情表初始化
        initializeDetail(signDetailInitializeBO);
        return newSignPlanId;
    }

    /**
     * 战规/动态
     *
     * @param newSignPlanId 新版签约计划版本id
     * @param spv           供货计划版本
     * @param alias      用户账户
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void initializeSignPlan(String newSignPlanId, SupplyPlanVersion spv, String alias) {
        // 供货版本id
        String supplyPlanVersionId = spv.getId();
        // 价格版本id
        String priceId = spv.getSourcePriceId();

        // 分期
        List<SupplyPlanVersionDetail> stagesList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(spv.getId(),
                IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_STAGE.getKey(), WhetherEnum.NO.getKey());
        // 组团
        List<SupplyPlanVersionDetail> groupList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(spv.getId(),
                IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_GROUP.getKey(), WhetherEnum.NO.getKey());
        // 产品构成
        List<SupplyPlanVersionDetail> productList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(spv.getId(),
                IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_PRODUCT.getKey(), WhetherEnum.NO.getKey());
        // 楼栋
        List<SupplyPlanVersionDetail> buildList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(spv.getId(),
                IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_BUILD.getKey(), WhetherEnum.YES.getKey());


        if (CollectionUtils.isEmpty(buildList)) {
            throw new MsgException(CodeEnum.NOT_BUILD_SALY_TYPE_ERROR);
        }
        if (CollectionUtils.isEmpty(stagesList)) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_DETAIL_ERROR);
        }
        if (CollectionUtils.isEmpty(groupList)) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_DETAIL_ERROR);
        }
        if (CollectionUtils.isEmpty(productList)) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_DETAIL_ERROR);
        }


        // 新的产品构成集合信息
        List<SignPlanVersionSupplyDetail> spvsdList = new ArrayList<>();
        // 将楼栋的层级提升到产品构成
        for (SupplyPlanVersionDetail p : productList) {
            String pid = p.getId();
            for (SupplyPlanVersionDetail b : buildList) {
                if (b.getPid().equals(pid)) {
                    //将不可租售与可租过滤
                    if (b.getBusinessType().equals(SalyTypeEnum.NOT_RENTABLE.getKey()) || b.getBusinessType().equals(SalyTypeEnum.RENTED.getKey())) {
                        continue;
                    }
                    SignPlanVersionSupplyDetail spvsd = BeanCopyUtils.getProductSignPlanVersionSupplyDetail(p, b);
                    // 签约计划版本id
                    spvsd.setVersionId(newSignPlanId);
                    spvsd.setOrgId(b.getOrgId());
                    // 结转日期
                    spvsd.setCarryOverTime(getTransferDate(priceId, b.getId()));
                    // 创建人员
                    spvsd.setCreateBy(alias);
                    // 创建时间
                    spvsd.setCreateTime(LocalDateTime.now());
                    spvsdList.add(spvsd);
                }
            }
        }
        if(CollectionUtils.isEmpty(spvsdList)){
            throw new MsgException(CodeEnum.NO_SALES_VALUE_ERROR);
        }
        for (SupplyPlanVersionDetail stages : stagesList) {
            SignPlanVersionSupplyDetail spvsd = BeanCopyUtils.getSignPlanVersionSupplyDetail(stages);
            // 签约计划版本id
            spvsd.setVersionId(newSignPlanId);
            // 创建人员
            spvsd.setCreateBy(alias);
            // 创建时间
            spvsd.setCreateTime(LocalDateTime.now());
            spvsdList.add(spvsd);
        }
        for (SupplyPlanVersionDetail group : groupList) {
            SignPlanVersionSupplyDetail spvsd = BeanCopyUtils.getSignPlanVersionSupplyDetail(group);
            // 签约计划版本id
            spvsd.setVersionId(newSignPlanId);
            // 创建人员
            spvsd.setCreateBy(alias);
            // 创建时间
            spvsd.setCreateTime(LocalDateTime.now());
            spvsdList.add(spvsd);
        }
        // 保存
        signPlanVersionSupplyDetailMapper.insertList(spvsdList);
        saveLog(LogEventEnum.ADD,"签约计划","签约计划创建","签约供货详情表新增数据成功,versionId:"+newSignPlanId);
    }

    /**
     * 投资版签约计划供货详情表初始化
     * @param signPlanVersionId 签约计划id
     * @param spv               供货计划id
     * @param alias          用户账户
     */
    @Transactional(rollbackFor = Exception.class)
    public void initializeCastDecisionSupplyDetail(String signPlanVersionId, SupplyPlanVersion spv, String alias) {
        // 供货版本id
        String supplyPlanVersionId = spv.getId();
        // 价格版本id
        String priceId = spv.getSourcePriceId();

        // 分期
        List<SupplyPlanVersionDetail> stagesList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(
                supplyPlanVersionId, IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_STAGE.getKey(), WhetherEnum.NO.getKey());
        // 组团
        List<SupplyPlanVersionDetail> groupList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(
                supplyPlanVersionId, IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_GROUP.getKey(), WhetherEnum.NO.getKey());
        // 批次
        List<SupplyPlanVersionDetail> batchList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(
                supplyPlanVersionId, IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_BATCH.getKey(), WhetherEnum.NO.getKey());
        // 产品构成
        List<SupplyPlanVersionDetail> productList = signPlanVersionMapper.selectSupplyByPlanDetailVersionId(
                supplyPlanVersionId, IsDeleteEnum.NO.getKey(), OrgTypeEunm.ORG_PRODUCT.getKey(),
                WhetherEnum.YES.getKey());

        if (CollectionUtils.isEmpty(productList)) {
            throw new MsgException(CodeEnum.NOT_BUILD_SALY_TYPE_ERROR);
        }
        if (CollectionUtils.isEmpty(stagesList)) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_DETAIL_ERROR);
        }
        if (CollectionUtils.isEmpty(groupList)) {
            throw new MsgException(CodeEnum.SUPPLY_PLAN_DETAIL_ERROR);
        }


        List<SignPlanVersionSupplyDetail> signPlanVersionSupplyDetailList = new ArrayList<>();

        // 组团
        for (SupplyPlanVersionDetail group : groupList) {
            // 批次
            for (SupplyPlanVersionDetail batch : batchList) {
                if (group.getId().equals(batch.getPid())) {
                    // 产品构成
                    for (SupplyPlanVersionDetail product : productList) {
                        if (batch.getId().equals(product.getPid())) {
                            //将不可租售与可租过滤
                            if (product.getBusinessType().equals(SalyTypeEnum.NOT_RENTABLE.getKey()) || product.getBusinessType().equals(SalyTypeEnum.RENTED.getKey())) {
                                continue;
                            }
                            // 是否车位楼栋
                            if (product.getIsParking().equals(WhetherEnum.YES.getKey())) {
                                if (product.getParkingNum() == 0) {
                                    continue;
                                }
                            }
                            SignPlanVersionSupplyDetail spvsd = BeanCopyUtils
                                    .getProductSignPlanVersionSupplyDetail(product, product);
                            // 父级id
                            spvsd.setPid(group.getId());
                            // 签约计划版本id
                            spvsd.setVersionId(signPlanVersionId);
                            // 结转日期
                            spvsd.setCarryOverTime(getTransferDate(priceId, product.getId()));
                            // 创建人员
                            spvsd.setCreateBy(alias);
                            // 创建时间
                            spvsd.setCreateTime(LocalDateTime.now());
                            signPlanVersionSupplyDetailList.add(spvsd);

                        }
                    }
                }
            }
        }
        if(CollectionUtils.isEmpty(signPlanVersionSupplyDetailList)){
            throw new MsgException(CodeEnum.NO_SALES_VALUE_ERROR);
        }
        for (SupplyPlanVersionDetail stages : stagesList) {
            SignPlanVersionSupplyDetail signPlanVersionSupplyDetail = BeanCopyUtils
                    .getSignPlanVersionSupplyDetail(stages);
            // 签约计划版本id
            signPlanVersionSupplyDetail.setVersionId(signPlanVersionId);
            // 创建人员
            signPlanVersionSupplyDetail.setCreateBy(alias);
            // 创建时间
            signPlanVersionSupplyDetail.setCreateTime(LocalDateTime.now());
            signPlanVersionSupplyDetailList.add(signPlanVersionSupplyDetail);
        }
        for (SupplyPlanVersionDetail group : groupList) {
            SignPlanVersionSupplyDetail signPlanVersionSupplyDetail = BeanCopyUtils
                    .getSignPlanVersionSupplyDetail(group);
            // 签约计划版本id
            signPlanVersionSupplyDetail.setVersionId(signPlanVersionId);
            // 创建人员
            signPlanVersionSupplyDetail.setCreateBy(alias);
            // 创建时间
            signPlanVersionSupplyDetail.setCreateTime(LocalDateTime.now());
            signPlanVersionSupplyDetailList.add(signPlanVersionSupplyDetail);
        }
        // 保存
        signPlanVersionSupplyDetailMapper.insertList(signPlanVersionSupplyDetailList);
        saveLog(LogEventEnum.ADD,"签约计划","签约计划创建","签约供货详情表新增数据成功,versionId:"+signPlanVersionId);
    }

    /**
     * 初始化预算版签约计划版本
     * @param supplyPlanVersion 新版预算版签约计划基于的签约计划（战规前：投资版,战规后：动态版/预算版）
     * @param versionIdentify       版本标识（预算版：mid_year--年中；all_year--全年）
     * @param compileYear           编制年份（创建预算版使用）
     * @param alias                 用户
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String insertBudgetSignPlanVersion(SupplyPlanVersion supplyPlanVersion,String versionAttr,String versionIdentify, String compileYear, String alias) {

        SignPlanVersion newSignPlanVersion = new SignPlanVersion();
        // 新版本id
        String signPlanVersionId = UUIDUtils.create();
        // 分期id
        String projectfId = supplyPlanVersion.getProjectfId();

        // 查询上一版签约计划
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectBudgetSignPlan(projectfId, VersionTypeEunm.BUDGET.getKey(),
                versionIdentify,VersionStatusEnum.SUCCESSAPPROVED.getKey(), IsDeleteEnum.NO.getKey());
        // 查询最新审批通过的签约版本
        if (null == oldSignPlanVersion) {
            newSignPlanVersion.setVersionNum(VersionUtils.bigVersionInc(null));
        } else {
            newSignPlanVersion.setVersionNum(VersionUtils.bigVersionInc(oldSignPlanVersion.getVersionNum()));
        }
        //是否一房一价
        newSignPlanVersion.setIsContainSale(isContainSale(projectfId));
        // 版本类型
        newSignPlanVersion.setVersionType(VersionTypeEunm.BUDGET.getKey());
        newSignPlanVersion.setId(signPlanVersionId);
        newSignPlanVersion.setProjectId(supplyPlanVersion.getProjectId());
        newSignPlanVersion.setProjectfId(projectfId);
        newSignPlanVersion.setStageCode(supplyPlanVersion.getStageCode());
        newSignPlanVersion.setStageName(supplyPlanVersion.getStageName());
        newSignPlanVersion.setVersionState(VersionStatusEnum.NOTPREPARED.getKey());
        newSignPlanVersion.setSourceSupplyId(supplyPlanVersion.getId());
        newSignPlanVersion.setSourceSupplyName(supplyPlanVersion.getVersionName());
        newSignPlanVersion.setAffiliationMonth(LocalDateTimeUtils.getLocalMonth(LocalDateTime.now()));
        newSignPlanVersion.setSourceSupplyNum(supplyPlanVersion.getVersionNum());
        newSignPlanVersion.setSourcePriceId(supplyPlanVersion.getSourcePriceId());
        newSignPlanVersion.setVersionAttr(versionAttr);
        newSignPlanVersion.setCompileYear(compileYear);
        newSignPlanVersion.setVersionIdentify(versionIdentify);
        newSignPlanVersion.setIsAssessmentVersion(0);
        newSignPlanVersion.setCreateBy(alias);
        newSignPlanVersion.setCreateTime(LocalDateTime.now());
        newSignPlanVersion.setVersionCreate(VersionCreateEnum.MANUALCREATE.getKey());
        newSignPlanVersion.setIsDelete(IsDeleteEnum.NO.getKey());
        signPlanVersionMapper.insert(newSignPlanVersion);
        saveLog(LogEventEnum.ADD,"签约计划","签约计划创建","签约版本表新增数据成功,versionId:"+signPlanVersionId);
        saveVersionLog(signPlanVersionId,VersionEventEnum.MANUALCREATE.getKey(),"",alias,"新建签约计划",null);
        return signPlanVersionId;
    }

    /**
     * 是否战规后
     */
    @Override
    public boolean isAfterWarplan(String projectFId) {
        List<String> priceVersionTypes = new ArrayList<>();
        priceVersionTypes.add(VersionTypeEunm.WARPLAN.getKey());
        priceVersionTypes.add(VersionTypeEunm.GETPRICE.getKey());
        priceVersionTypes.add(VersionTypeEunm.DYNAMIC.getKey());
        List<PriceVersion> supplyPlanVersionList = signPlanVersionMapper.selectPriceByVersionTypes(projectFId,
                priceVersionTypes, IsDeleteEnum.NO.getKey());
        if ( CollectionUtils.isNotEmpty(supplyPlanVersionList)) {
            return true;
        }
        return false;
    }

    /**
     * 是否战规后
     */
    public boolean isAfterWarplan(String projectFId,String yearMonth) {
        List<String> priceVersionTypes = new ArrayList<>();
        priceVersionTypes.add(VersionTypeEunm.WARPLAN.getKey());
        priceVersionTypes.add(VersionTypeEunm.GETPRICE.getKey());
        priceVersionTypes.add(VersionTypeEunm.DYNAMIC.getKey());
        List<PriceVersion> supplyPlanVersionList = signPlanVersionMapper.selectPriceByVersionTypesAndTime(projectFId,
                priceVersionTypes,yearMonth,IsDeleteEnum.NO.getKey());
        if (supplyPlanVersionList.size() > 0) {
            return true;
        }
        return false;
    }

    /**
     * 获取战规后版本类型 战规后(动态版,战规后预算版,战规版)
     *
     * @return
     */
    public List<String> getAfterVersionTypes() {
        List<String> str = new ArrayList<>();
        str.add(VersionTypeEunm.WARPLAN.getKey());
        str.add(VersionTypeEunm.DYNAMIC.getKey());
        str.add(VersionTypeEunm.BUDGET.getKey());
        return str;
    }


    /**
     * 基于供货计划对签约版本初始化并保存
     *
     * @param spv         供货计划版本
     * @param alias    用户账户
     * @param versionType 版本类型
     * @param versionCreate 创建方式
     * @return 新签约版本id
     */
    @Transactional(rollbackFor = Exception.class)
    public String insertVersion(SupplyPlanVersion spv,String versionIdentify, String alias, String versionType,String versionCreate) {
        SignPlanVersion newSignPlanVersion = new SignPlanVersion();

        // 新版本id
        String signPlanVersionId = UUIDUtils.create();
        // 分期id
        String projectfId = spv.getProjectfId();
        // 获取版本号
        String versionNum = getVersionNum(projectfId,versionType);

        // 基于“投资考核版”供货计划编制的第一版投资版签约计划默认作为“投资考核版”签约计划
        if (versionType.equals(VersionTypeEunm.INVESTMENT.getKey())) {
            if(spv.getIsAssessmentVersion() !=null && spv.getIsAssessmentVersion() == WhetherEnum.YES.getKey()){
                //查询当前分期下是否存在投资考核版签约计划
                int count = signPlanVersionMapper.selectIsAssessmentByProjectfId(projectfId,VersionTypeEunm.INVESTMENT.getKey(),WhetherEnum.YES.getKey(),IsDeleteEnum.NO.getKey());
                if(count == 0){
                    newSignPlanVersion.setIsAssessmentVersion(1);
                }else {
                    newSignPlanVersion.setIsAssessmentVersion(0);
                }
            }
            newSignPlanVersion.setVersionAttr(VersionAttrEnum.BEFORE_WARPLAN.getKey());
        }else {
            newSignPlanVersion.setVersionAttr(VersionAttrEnum.AFTER_WARPLAN.getKey());
        }


        newSignPlanVersion.setVersionNum(versionNum);
        //是否一房一价
        newSignPlanVersion.setIsContainSale(isContainSale(projectfId));
        // 主键id
        newSignPlanVersion.setId(signPlanVersionId);
        // 项目id
        newSignPlanVersion.setProjectId(spv.getProjectId());
        // 分期id
        newSignPlanVersion.setProjectfId(spv.getProjectfId());
        // 分期Code
        newSignPlanVersion.setStageCode(spv.getStageCode());
        // 分期名称
        newSignPlanVersion.setStageName(spv.getStageName());
        // 版本状态
        newSignPlanVersion.setVersionState(VersionStatusEnum.NOTPREPARED.getKey());
        // 版本类型
        newSignPlanVersion.setVersionType(versionType);
        // 版本来源id
        newSignPlanVersion.setSourceSupplyId(spv.getId());
        // 版本来源名称
        newSignPlanVersion.setSourceSupplyName(spv.getVersionName());
        // 版本来源版本号
        newSignPlanVersion.setSourceSupplyNum(spv.getVersionNum());
        //版本来源-货值版本Id
        newSignPlanVersion.setSourcePriceId(spv.getSourcePriceId());

        if (LocalDateTimeUtils.isBetween0And6()) {
            // 版本归属时间
            newSignPlanVersion.setAffiliationMonth(LocalDateTimeUtils.getLocalMonth(LocalDateTime.now().minusMonths(1L)));
        } else {
            // 版本归属时间
            newSignPlanVersion.setAffiliationMonth(LocalDateTimeUtils.getLocalMonth(LocalDateTime.now()));
        }
        // 创建人员
        newSignPlanVersion.setCreateBy(alias);
        // 创建时间
        newSignPlanVersion.setCreateTime(LocalDateTime.now());
        newSignPlanVersion.setVersionCreate(versionCreate);
        newSignPlanVersion.setIsDelete(IsDeleteEnum.NO.getKey());
        //设置预算标签
        if(StrUtils.isNotEmpty(versionIdentify)) {
        	newSignPlanVersion.setVersionIdentify(versionIdentify);
        }

        signPlanVersionMapper.insert(newSignPlanVersion);
        saveLog(LogEventEnum.ADD,"签约计划","签约计划创建","签约版本表新增数据成功,versionId:"+signPlanVersionId);
        saveVersionLog(signPlanVersionId,VersionEventEnum.MANUALCREATE.getKey(),"",alias,"新建签约计划",null);
        return signPlanVersionId;
    }

    /**
     * 获取版本号
     * @param projectfId 分期id
     * @param versionType 版本类型
     * @return
     */
    private String getVersionNum(String projectfId, String versionType) {
        String versionNum = null;

        List<String> versionTypes = Arrays.asList(versionType);
        List<String> versionStatuss = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        // 查询最新审批通过的签约版本
        SignPlanVersion oldSignPlanVersion = signPlanVersionMapper.selectNewesVersion(projectfId, versionTypes, versionStatuss, IsDeleteEnum.NO.getKey());
        //版本号赋值处理
        if (null != oldSignPlanVersion) {
            if (VersionTypeEunm.DYNAMIC.getKey().equals(versionType)) {
                if (oldSignPlanVersion.getAffiliationMonth().equals(LocalDateTimeUtils.getLocalMonth(LocalDateTime.now()))) {
                    versionNum = oldSignPlanVersion.getVersionNum();
                }
            }else {
                versionNum = oldSignPlanVersion.getVersionNum();
            }
        }
        return VersionUtils.bigVersionInc(versionNum);
    }

    /**
     * 签约计划详情表初始化
     *
     * @param newSignPlanId      新建签约计划版本id
     * @param alias             `用户
     * @param oldSignPlanVersion 被复制的签约计划
     * @param versionType        版本类型
     * @param projectFid         分期id
     */
    @Transactional(rollbackFor = Exception.class)
    public void initializeDetail(SignDetailInitializeBO signDetailInitializeBO) {
        List<SignPlanVersionDetail> list = new ArrayList<>();

        String newSignPlanId = signDetailInitializeBO.getVersionId();
        String versionType = signDetailInitializeBO.getVersionType();
        SignPlanVersion oldSignPlanVersion = signDetailInitializeBO.getOldSignPlanVersion();
        String projectFid = signDetailInitializeBO.getProjectFid();
        String versionAttr = signDetailInitializeBO.getVersionAttr();
        String alias = signDetailInitializeBO.getAlias();

        SignProductCodeBO oldSignProductCodeBO = new SignProductCodeBO();
        //签约计划详情模板
        SignPlanVersionDetail spvdTemplate = new SignPlanVersionDetail();
        spvdTemplate.setVersionId(newSignPlanId);
        spvdTemplate.setCreateBy(alias);
        spvdTemplate.setCreateTime(LocalDateTime.now());
        //上一版签约计划的签约详情数据集合
        List<SignPlanVersionDetail> oldSpvdList = new ArrayList<>();

        //确定版本所属时间
        LocalDateTime now = LocalDateTime.now();
        if (LocalDateTimeUtils.isBetween0And6()) {
            now = now.minusMonths(1L);
        }
        //获取签约计划倒挤数据
        SignSqueezeBO signSqueezeBO = getSignSqueezeBO(oldSignPlanVersion,now,versionType);

        // 查询版本下全部组团
        List<SignPlanVersionSupplyDetail> groupList = signPlanVersionSupplyDetailMapper.selectByType(newSignPlanId, OrgTypeEunm.ORG_GROUP.getKey(), IsDeleteEnum.NO.getKey());
        for (SignPlanVersionSupplyDetail group : groupList) {
            String pid = group.getSupplyPlanId();
            // 查询组团下有多少种业态（产品构成+经营方式+装修方式+产品属性）
            List<SignProductCodeBO> signProductCodeBOs = signPlanVersionSupplyDetailMapper.selectProductCodes(newSignPlanId, pid, IsDeleteEnum.NO.getKey());
            // 对该分期组团下产品构成做初始化
            for (SignProductCodeBO signProductCodeBO : signProductCodeBOs) {
                // 初始化签约计划详情查询条件
                signProductCodeBO.setVersionId(newSignPlanId);
                signProductCodeBO.setPid(pid);
                signProductCodeBO.setIsDel(IsDeleteEnum.NO.getKey());
                // 查询组团下产品构成类型供货详情列表
                List<SignPlanVersionSupplyDetail> supplyDetailList = signPlanVersionSupplyDetailMapper.selectSupplyPlanDetail(signProductCodeBO);
                if (CollectionUtils.isEmpty(supplyDetailList)) {
                    continue;
                }
                SignPlanVersionSupplyDetail spvsd = supplyDetailList.get(0);
                // 初始化签约计划详情共有数据模板
                spvdTemplate = initializeSignDetailTemplate(spvdTemplate,spvsd,group);
                // 是否车位楼栋（0否,1是）
                Integer isParking = spvsd.getIsParking();

                // 取签约供货详情,有实际取实际，无实际取计划
                List<SignSupplyDetailBO> ssdBOList = switchSignSupplyDetailBO(supplyDetailList);
                //获取实际签约数据相关参数
                SignActualCalculateBO signActualCalculateBO = getSignActualCalculateBO(ssdBOList,versionAttr,projectFid);
                // 初始化上一版签约计划详情查询条件
                if (oldSignPlanVersion != null) {
                    oldSignProductCodeBO.setVersionId(oldSignPlanVersion.getId());
                    oldSignProductCodeBO.setPid(group.getOrgId());
                    oldSignProductCodeBO.setProductCode(signProductCodeBO.getProductCode());
                    oldSignProductCodeBO.setBusinessType(signProductCodeBO.getBusinessType());
                    oldSignProductCodeBO.setFreeType(signProductCodeBO.getFreeType());
                    oldSignProductCodeBO.setProType(signProductCodeBO.getProType());
                    oldSignProductCodeBO.setIsDel(IsDeleteEnum.NO.getKey());
                    oldSpvdList = signPlanVersionDetailMapper.selectSignDetail(oldSignProductCodeBO);
                }

                //取供货计划日期、供货实际日期、虚拟供货日期中最早的日期
                LocalDateTime earliestSupplyDate = getProductEarliestTime(supplyDetailList);
                //取供货计划日期、倒挤日期、上一版签约计划签约日期中最晚的日期
                LocalDateTime latestSupplyDate = getProductlatestTime(oldSignPlanVersion,signProductCodeBO,signSqueezeBO,oldSignProductCodeBO);

                // 记录月初存货
                SignPlanVersionDetailBO spvdBO = new SignPlanVersionDetailBO();
                spvdBO.initialize();

                List<SignPlanVersionDetail> productList = new ArrayList<>();
                // 从最早供货日期至最晚供货日期,按月份初始化
                for (LocalDateTime time = earliestSupplyDate; LocalDateTimeUtils.compareDate(time, latestSupplyDate, 1); time = LocalDateTimeUtils.addDate(time)) {
                    SignPlanVersionDetail newSignDetail = new SignPlanVersionDetail();
                    try {
                        newSignDetail = spvdTemplate.clone();
                    } catch (CloneNotSupportedException e) {
                        log.error("###### errorMsg: ", e);
                    }
                    newSignDetail.setId(UUIDUtils.create()); // 主键

                    // 是否早于当前时间
                    boolean isBefore = LocalDateTimeUtils.compareDate(time, now, 1);

                    String yearMonth = LocalDateTimeUtils.formatTime(time,"yyyy-MM");
                    List<SignSupplyDetailBO> monthSupplyDetailList = getSignPlanVersionSupplyDetailBySuppluTime(ssdBOList, yearMonth);
                    newSignDetail.setEarlyRoomCount(spvdBO.getEarlyRoomCount());
                    newSignDetail.setEarlyRoomArea(spvdBO.getEarlyRoomArea());
                    newSignDetail.setEarlyAvgRoomArea(spvdBO.getEarlyAvgRoomArea());
                    newSignDetail.setEarlyAvgPrice(spvdBO.getEarlyAvgPrice());
                    newSignDetail.setEarlySumPrice(spvdBO.getEarlySumPrice());
                    newSignDetail.setEarlyCanSignRoomCount(newSignDetail.getEarlyRoomCount().subtract(newSignDetail.getEarlySignRoomCount()));
                    // 签约计划时间默认当月1号(xxxx-xx-01)
                    newSignDetail.setSignPlanTime(LocalDateTimeUtils.toLocalDateTime(yearMonth));

                    //签约计划初始化参数
                    SignInitializeBO signInitializeBO = new SignInitializeBO();
                    signInitializeBO.setSignPlanVersionDetail(newSignDetail);
                    signInitializeBO.setSignSupplyDetailList(monthSupplyDetailList);
                    signInitializeBO.setIsParking(isParking);
                    signInitializeBO.setVersionType(versionType);
                    signInitializeBO.setYearMonth(yearMonth);
                    signInitializeBO.setIsBefore(isBefore);

                    //初始化签约实际数据
                    initializeEarlySignDetail(signInitializeBO,signActualCalculateBO);
                    //初始化本月新供数据
                    initializeSupplySignDetail(signInitializeBO);
                    //复制上一版签约计划
                    copyOldVersionSignPlan(signInitializeBO,oldSpvdList,signSqueezeBO);

                    // 是否早于当前月份
                    isBefore = LocalDateTimeUtils.compareDate(time, now, 0);
                    signInitializeBO.setIsBefore(isBefore);
                    //计算下个月月初存货
                    calculateMonthlyInventory(spvdBO,signInitializeBO);

                    productList.add(newSignDetail);

                    //将套数、面积差的差异放到最后一个月
                    if(LocalDateTimeUtils.compareDate(time, latestSupplyDate,2)){
                        //判断是否业态金额是否编辑完成
                        if(spvdBO.getEarlySumPrice().compareTo(BigDecimal.ZERO) == 0){
                            newSignDetail.setEarlySignRoomCount(newSignDetail.getEarlyRoomCount());
                            newSignDetail.setEarlyAvgPrice(newSignDetail.getEarlyAvgPrice());
                            newSignDetail.setEarlySignRoomArea(newSignDetail.getEarlyRoomArea());
                            newSignDetail.setEarlyCanSignRoomCount(BigDecimal.ZERO);
                            newSignDetail.setSupplySignAvgPrice(newSignDetail.getSupplyAvgPrice());
                            newSignDetail.setSupplySignRoomArea(newSignDetail.getSupplyRoomArea());
                            newSignDetail.setSupplySignRoomCount(newSignDetail.getSupplyRoomCount());
                            newSignDetail.setSupplyCanSignRoomCount(BigDecimal.ZERO);
                            //产品构成的剩余可签套数、面积、金额为0,直接将编制状态改为已完成
                            productList.forEach(s->s.setIsEstablishment(1));
                        }
                    }
                }
                list.addAll(productList);
            }
        }
        // 保存该产品构成的签约计划明细列表
        if (CollectionUtils.isNotEmpty(list)) {
            List<SignPlanVersionDetail> collect = list.stream().filter(s -> s.getIsEstablishment() == 1).collect(Collectors.toList());
            //如果有产品构成是编制完成状态,将新建版本的状态设置为'编制中'
            if(CollectionUtils.isNotEmpty(collect)){
                SignPlanVersion signPlanVersion = signPlanVersionMapper.selectById(newSignPlanId);

                //变更前版本状态
                String beforeState = signPlanVersion.getVersionState();

                signPlanVersion.setVersionState(VersionStatusEnum.ORGANIZATION.getKey());
                signPlanVersionMapper.updateById(signPlanVersion);

                saveVersionLog(newSignPlanId,VersionEventEnum.MANUALCREATE.getKey(),beforeState,alias,"新建签约计划","如果所有产品构成都是编制完成状态,将新建版本的状态设置为'编制中'");
            }
            signPlanVersionDetailMapper.insertList(list);
            saveLog(LogEventEnum.ADD,"签约计划","签约计划创建","签约详情表新增数据成功,versionId:"+newSignPlanId+"/"+list);
        }

        //若是动态版,接收销管数据倒挤
        if (versionType.equals(VersionTypeEunm.DYNAMIC.getKey())) {
            Map<Boolean, String> map = insertDynamicSignPlanVersion(projectFid);
        }
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectById(newSignPlanId);
        String supplyId = signPlanVersion.getSourceSupplyId();
        if(versionType.equals(VersionTypeEunm.INVESTMENT.getKey()) || versionType.equals(VersionTypeEunm.WARPLAN.getKey())){
            IdmUser idmUser = idmUserService.getByAlias(alias);
            //消除代办
            deleteTodoInfo(projectFid + versionType,OperatorTypeEnum.TRANSFERRED_TO_OFFICE.getKey(),idmUser);
        }

    }


    /**
     * 计算下个月月初存货
     * @param spvdBO 月初存货
     * @param signInitializeBO 签约计划初始化参数
     */
    @Override
    public void calculateMonthlyInventory(SignPlanVersionDetailBO spvdBO, SignInitializeBO signInitializeBO) {
        SignPlanVersionDetail newSignDetail = signInitializeBO.getSignPlanVersionDetail();
        Integer isParking = signInitializeBO.getIsParking();
        String versionType = signInitializeBO.getVersionType();
        boolean isBefore = signInitializeBO.getIsBefore();


        //计算当月剩余可签套数
        if (LocalDateTimeUtils.compareDate(newSignDetail.getSignPlanTime(), LocalDateTime.now(), 0)) {
            // <月初存货>剩余可签套数 = <月初存货>套数 - <月初存货-本月签约>实际套数
            newSignDetail.setEarlyCanSignRoomCount(newSignDetail.getEarlyRoomCount().subtract(BigDecimal.valueOf(newSignDetail.getEarlySignRoomCountActual())));
            // <本月新供>剩余可签套数 = <本月新供>套数 - <本月新供-本月签约>实际套数
            newSignDetail.setSupplyCanSignRoomCount(newSignDetail.getSupplyRoomCount().subtract(BigDecimal.valueOf(newSignDetail.getSupplySignRoomCountActual())));
        } else {
            // <月初存货>剩余可签套数 = <月初存货>套数 - <月初存货-本月签约>套数
            newSignDetail.setEarlyCanSignRoomCount(newSignDetail.getEarlyRoomCount().subtract(newSignDetail.getEarlySignRoomCount()));
            // <本月新供>剩余可签套数=<本月新供>套数 - <本月新供-本月签约>套数
            newSignDetail.setSupplyCanSignRoomCount(newSignDetail.getSupplyRoomCount().subtract(newSignDetail.getSupplySignRoomCount()));
        }

        // 下个月初存货的套数 = <月初存货>剩余可签套数 + <本月新供>剩余可签套数
        spvdBO.setEarlyRoomCount(newSignDetail.getEarlyCanSignRoomCount().add(newSignDetail.getSupplyCanSignRoomCount()));
        if(LocalDateTimeUtils.compareDate(newSignDetail.getSignPlanTime(), LocalDateTime.now(), 0)){
            // 下个月初存货的金额 = <月初存货>金额 +<本月新供>金额 - <月初存货-本月签约>金额-实际 - <本月新供-本月签约>金额-实际
            spvdBO.setEarlySumPrice(newSignDetail.getEarlySumPrice().add(newSignDetail.getSupplySumPrice())
                    .subtract(newSignDetail.getEarlySignSumPriceActual())
                    .subtract(newSignDetail.getSupplySignSumPriceActual()));
            if (isParking.equals(WhetherEnum.YES.getKey())) {
                // 下个月初存货的均价 = 下个月初存货的金额 / 下个月初存货的套数
                spvdBO.setEarlyAvgPrice(
                        BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomCount(), 4));
            } else {
                // 下个月初存货的面积 = <月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>实际面积 - <本月新供-本月签约>实际面积
                spvdBO.setEarlyRoomArea(
                        newSignDetail.getEarlyRoomArea().add(newSignDetail.getSupplyRoomArea())
                                .subtract(newSignDetail.getEarlySignRoomAreaActual())
                                .subtract(newSignDetail.getSupplySignRoomAreaActual()));
                // 下个月初存货的套均面积 = 下个月初存货的面积 / 下个月初存货的套数
                spvdBO.setEarlyAvgRoomArea(BigDecimalUtils.divBigDecimal(spvdBO.getEarlyRoomArea(), spvdBO.getEarlyRoomCount(), 4));
                // 下个月初存货的均价=下个月初存货的金额 / 下个月初存货的面积
                spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomArea(), 4));
            }

        }else {
            // 下个月初存货的金额 = <月初存货>金额 + <本月新供>金额 - <月初存货-本月签约>计划金额 - <本月新供-本月签约>计划金额
            spvdBO.setEarlySumPrice(newSignDetail.getEarlySumPrice().add(newSignDetail.getSupplySumPrice())
                    .subtract(newSignDetail.getEarlySignSumPrice())
                    .subtract(newSignDetail.getSupplySignSumPrice()));
            if (isParking.equals(WhetherEnum.YES.getKey())) {
                // 下个月初存货的均价 = 下个月初存货的金额 / 下个月初存货的套数
                spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomCount(), 4));
            } else {
                // 下个月初存货的面积=<月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>计划面积 - <本月新供-本月签约>计划面积
                spvdBO.setEarlyRoomArea(
                        newSignDetail.getEarlyRoomArea().add(newSignDetail.getSupplyRoomArea())
                                .subtract(newSignDetail.getEarlySignRoomArea())
                                .subtract(newSignDetail.getSupplySignRoomArea()));
                // 下个月初存货的套均面积 = 下个月初存货的面积/下个月初存货的套数
                spvdBO.setEarlyAvgRoomArea(BigDecimalUtils.divBigDecimal(spvdBO.getEarlyRoomArea(), spvdBO.getEarlyRoomCount(), 4));
                // 下个月初存货的均价=下个月初存货的金额 / 下个月初存货的面积
                spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomArea(), 4));
            }
        }

        /*
        //计算当月剩余可签套数
        if (isBefore && !versionType.equals(VersionTypeEunm.INVESTMENT.getKey())) {
            // <月初存货>剩余可签套数 = <月初存货>套数 - <月初存货-本月签约>实际套数
            newSignDetail.setEarlyCanSignRoomCount(newSignDetail.getEarlyRoomCount().subtract(BigDecimal.valueOf(newSignDetail.getEarlySignRoomCountActual())));
            // <本月新供>剩余可签套数 = <本月新供>套数 - <本月新供-本月签约>实际套数
            newSignDetail.setSupplyCanSignRoomCount(newSignDetail.getSupplyRoomCount().subtract(BigDecimal.valueOf(newSignDetail.getSupplySignRoomCountActual())));
        } else {
            // <月初存货>剩余可签套数 = <月初存货>套数 - <月初存货-本月签约>套数
            newSignDetail.setEarlyCanSignRoomCount(newSignDetail.getEarlyRoomCount().subtract(newSignDetail.getEarlySignRoomCount()));
            // <本月新供>剩余可签套数=<本月新供>套数 - <本月新供-本月签约>套数
            newSignDetail.setSupplyCanSignRoomCount(newSignDetail.getSupplyRoomCount().subtract(newSignDetail.getSupplySignRoomCount()));
        }

        if(versionType.equals(VersionTypeEunm.INVESTMENT.getKey())){
            // 下个月初存货的套数 = <月初存货>剩余可签套数 + <本月新供>剩余可签套数
            spvdBO.setEarlyRoomCount(newSignDetail.getEarlyCanSignRoomCount()
                    .add(newSignDetail.getSupplyCanSignRoomCount()));
            // 下个月初存货的金额 = <月初存货>金额 + <本月新供>金额 - <月初存货-本月签约>计划金额 - <本月新供-本月签约>计划金额
            spvdBO.setEarlySumPrice(newSignDetail.getEarlySumPrice().add(newSignDetail.getSupplySumPrice())
                    .subtract(newSignDetail.getEarlySignSumPrice())
                    .subtract(newSignDetail.getSupplySignSumPrice()));
            if (isParking.equals(WhetherEnum.YES.getKey())) {
                // 下个月初存货的均价 = 下个月初存货的金额 / 下个月初存货的套数
                spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomCount(), 4));
            } else {
                // 下个月初存货的面积=<月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>计划面积 - <本月新供-本月签约>计划面积
                spvdBO.setEarlyRoomArea(
                        newSignDetail.getEarlyRoomArea().add(newSignDetail.getSupplyRoomArea())
                                .subtract(newSignDetail.getEarlySignRoomArea())
                                .subtract(newSignDetail.getSupplySignRoomArea()));
                // 下个月初存货的套均面积 = 下个月初存货的面积/下个月初存货的套数
                spvdBO.setEarlyAvgRoomArea(BigDecimalUtils.divBigDecimal(spvdBO.getEarlyRoomArea(), spvdBO.getEarlyRoomCount(), 4));
                // 下个月初存货的均价=下个月初存货的金额 / 下个月初存货的面积
                spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomArea(), 4));
            }

        }else {
            // 下个月初存货的套数 = <月初存货>剩余可签套数 + <本月新供>剩余可签套数
            spvdBO.setEarlyRoomCount(newSignDetail.getEarlyCanSignRoomCount()
                    .add(newSignDetail.getSupplyCanSignRoomCount()));
            if (isBefore && !versionType.equals(VersionTypeEunm.INVESTMENT.getKey())) {
                // 下个月初存货的金额 = <月初存货>金额 +<本月新供>金额 - <月初存货-本月签约>金额-实际 - <本月新供-本月签约>金额-实际
                spvdBO.setEarlySumPrice(newSignDetail.getEarlySumPrice().add(newSignDetail.getSupplySumPrice())
                        .subtract(newSignDetail.getEarlySignSumPriceActual())
                        .subtract(newSignDetail.getSupplySignSumPriceActual()));
                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    // 下个月初存货的均价 = 下个月初存货的金额 / 下个月初存货的套数
                    spvdBO.setEarlyAvgPrice(
                            BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomCount(), 4));
                } else {
                    // 下个月初存货的面积 = <月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>实际面积 - <本月新供-本月签约>实际面积
                    spvdBO.setEarlyRoomArea(
                            newSignDetail.getEarlyRoomArea().add(newSignDetail.getSupplyRoomArea())
                                    .subtract(newSignDetail.getEarlySignRoomAreaActual())
                                    .subtract(newSignDetail.getSupplySignRoomAreaActual()));
                    // 下个月初存货的套均面积 = 下个月初存货的面积 / 下个月初存货的套数
                    spvdBO.setEarlyAvgRoomArea(BigDecimalUtils.divBigDecimal(spvdBO.getEarlyRoomArea(), spvdBO.getEarlyRoomCount(), 4));
                    // 下个月初存货的均价=下个月初存货的金额 / 下个月初存货的面积
                    spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomArea(), 4));
                }

            } else {
                // 下个月初存货的金额 = <月初存货>金额 + <本月新供>金额 - <月初存货-本月签约>计划金额 - <本月新供-本月签约>计划金额
                spvdBO.setEarlySumPrice(newSignDetail.getEarlySumPrice().add(newSignDetail.getSupplySumPrice())
                        .subtract(newSignDetail.getEarlySignSumPrice())
                        .subtract(newSignDetail.getSupplySignSumPrice()));
                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    // 下个月初存货的均价 = 下个月初存货的金额 / 下个月初存货的套数
                    spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomCount(), 4));
                } else {
                    // 下个月初存货的面积=<月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>计划面积 - <本月新供-本月签约>计划面积
                    spvdBO.setEarlyRoomArea(
                            newSignDetail.getEarlyRoomArea().add(newSignDetail.getSupplyRoomArea())
                                    .subtract(newSignDetail.getEarlySignRoomArea())
                                    .subtract(newSignDetail.getSupplySignRoomArea()));
                    // 下个月初存货的套均面积 = 下个月初存货的面积/下个月初存货的套数
                    spvdBO.setEarlyAvgRoomArea(BigDecimalUtils.divBigDecimal(spvdBO.getEarlyRoomArea(), spvdBO.getEarlyRoomCount(), 4));
                    // 下个月初存货的均价=下个月初存货的金额 / 下个月初存货的面积
                    spvdBO.setEarlyAvgPrice(BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomArea(), 4));
                }
            }
        }
        */
    }


    /**
     * 取供货计划日期、倒挤日期、上一版签约计划签约日期中最晚的日期
     * @param oldSignPlanVersion 上一版签约计划
     * @param signProductCodeBO
     * @param signSqueezeBO
     * @param oldSignProductCodeBO
     * @return
     */
    private LocalDateTime getProductlatestTime(SignPlanVersion oldSignPlanVersion, SignProductCodeBO signProductCodeBO, SignSqueezeBO signSqueezeBO, SignProductCodeBO oldSignProductCodeBO) {
        // 查询该分期组团下产品构成的最晚供货计划日期
        LocalDateTime latestSupplyDate = signPlanVersionSupplyDetailMapper.selectLatestSupplyDate(signProductCodeBO);
        LocalDateTime now = LocalDateTime.now();
        if (latestSupplyDate.isBefore(now)) {
            latestSupplyDate = now;
        }
        // 若最晚供货计划时间小于倒挤月份，则等于倒挤月份
        if(null != signSqueezeBO){
            String squeezeYearMonth = signSqueezeBO.getSqueezeYearMonth();
            LocalDateTime squeezeYearMonthTime = LocalDateTimeUtils.toLocalDateTime(squeezeYearMonth);
            if(signSqueezeBO.getIsSqueeze()){
                if (latestSupplyDate.isBefore(squeezeYearMonthTime)) {
                    latestSupplyDate = squeezeYearMonthTime;
                }
            }
        }

        //若存在上一版签约计划,最晚时间若早于上一版签约计划最晚签约时间,取最晚签约时间
        if (oldSignPlanVersion != null) {
            LocalDateTime latestSignTime = signPlanVersionDetailMapper.selectLatestSignDateByProduct(oldSignProductCodeBO);
            if (latestSignTime != null && latestSupplyDate.isBefore(latestSignTime)) {
                latestSupplyDate = latestSignTime;
            }
        }
        return latestSupplyDate;

    }

    /**
     * 初始化签约计划详情共有数据模板
     * @param spvdTemplate 签约计划详情模板
     * @param spvsd 签约供货详情
     * @param group 组团信息
     * @return
     */
    @Override
    public SignPlanVersionDetail initializeSignDetailTemplate(SignPlanVersionDetail spvdTemplate, SignPlanVersionSupplyDetail spvsd, SignPlanVersionSupplyDetail group) {

        // 是否车位楼栋（0否,1是）
        Integer isParking = spvsd.getIsParking();

        spvdTemplate.setProductType(spvsd.getType());
        spvdTemplate.setProductCode(spvsd.getOrgCode());
        spvdTemplate.setProductName(spvsd.getOrgName());
        spvdTemplate.setBusinessType(spvsd.getBusinessType());
        spvdTemplate.setBusinessTypeName(spvsd.getBusinessTypeName());
        spvdTemplate.setFreeType(spvsd.getFreeType());
        spvdTemplate.setFreeTypeName(spvsd.getFreeTypeName());
        spvdTemplate.setProType(spvsd.getProType());
        spvdTemplate.setProTypeName(spvsd.getProTypeName());
        spvdTemplate.setProProductCode(spvsd.getProProductCode());
        spvdTemplate.setProProductType(spvsd.getProProductType());
        spvdTemplate.setIsParking(isParking);
        spvdTemplate.setGroupId(group.getOrgId());
        spvdTemplate.setGroupType(group.getType());
        spvdTemplate.setGroupCode(group.getOrgCode());
        spvdTemplate.setGroupName(group.getOrgName());
        spvdTemplate.setEarlySignRoomCount(BigDecimal.ZERO);
        spvdTemplate.setEarlySignRoomArea(BigDecimal.ZERO);
        spvdTemplate.setEarlySignAvgPrice(BigDecimal.ZERO);
        spvdTemplate.setEarlySignSumPrice(BigDecimal.ZERO);
        spvdTemplate.setSupplySignRoomCount(BigDecimal.ZERO);
        spvdTemplate.setSupplySignRoomArea(BigDecimal.ZERO);
        spvdTemplate.setSupplySignAvgPrice(BigDecimal.ZERO);
        spvdTemplate.setSupplySignSumPrice(BigDecimal.ZERO);
        spvdTemplate.setEarlySignRoomCountActual(0);
        spvdTemplate.setEarlySignRoomAreaActual(BigDecimal.ZERO);
        spvdTemplate.setEarlySignAvgPriceActual(BigDecimal.ZERO);
        spvdTemplate.setEarlySignSumPriceActual(BigDecimal.ZERO);
        spvdTemplate.setSupplySignRoomCountActual(0);
        spvdTemplate.setSupplySignRoomAreaActual(BigDecimal.ZERO);
        spvdTemplate.setSupplySignAvgPriceActual(BigDecimal.ZERO);
        spvdTemplate.setSupplySignSumPriceActual(BigDecimal.ZERO);
        spvdTemplate.setYearEalySignRoomCountActual(0);
        spvdTemplate.setYearEalySignRoomAreaActual(BigDecimal.ZERO);
        spvdTemplate.setYearEalySignAvgPriceActual(BigDecimal.ZERO);
        spvdTemplate.setYearEalySignSumPriceActual(BigDecimal.ZERO);
        spvdTemplate.setEarlyRoomCount(BigDecimal.ZERO);
        spvdTemplate.setEarlyCanSignRoomCount(BigDecimal.ZERO);
        spvdTemplate.setEarlyRoomArea(BigDecimal.ZERO);
        spvdTemplate.setEarlyAvgRoomArea(BigDecimal.ZERO);
        spvdTemplate.setEarlyAvgPrice(BigDecimal.ZERO);
        spvdTemplate.setEarlySumPrice(BigDecimal.ZERO);
        spvdTemplate.setSupplyRoomCount(BigDecimal.ZERO);
        spvdTemplate.setSupplyCanSignRoomCount(BigDecimal.ZERO);
        spvdTemplate.setSupplyRoomArea(BigDecimal.ZERO);
        spvdTemplate.setSupplyAvgRoomArea(BigDecimal.ZERO);
        spvdTemplate.setSupplyAvgPrice(BigDecimal.ZERO);
        spvdTemplate.setSupplySumPrice(BigDecimal.ZERO);
        spvdTemplate.setIsEstablishment(WhetherEnum.NO.getKey());
        spvdTemplate.setIsLock(WhetherEnum.NO.getKey());
        spvdTemplate.setIsDelete(WhetherEnum.NO.getKey());

        return spvdTemplate;
    }

    /**
     * 获取签约计划倒挤数据
     * @return
     * @param oldSignPlanVersion 上一版签约版本
     * @param now 版本归属时间
     * @param versionType 版本类型
     */
    private SignSqueezeBO getSignSqueezeBO(SignPlanVersion oldSignPlanVersion, LocalDateTime dateTime, String versionType) {
        SignSqueezeBO signSqueezeBO = new SignSqueezeBO();
        //差异年月
        String differenceYearMonth = LocalDateTimeUtils.formatTime(dateTime.minusMonths(1L), "yyyy-MM");
        //是否做倒挤操作
        boolean isSqueeze = false;
        if(null != oldSignPlanVersion){
            //判断oldSignPlanVersion是否是上个月的版本
            if(oldSignPlanVersion.getAffiliationMonth().equals(differenceYearMonth)){
                //是否动态版
                if(versionType.equals(VersionTypeEunm.DYNAMIC.getKey())){
                    isSqueeze = true;
                }
            }
        }
        // 倒挤月份
        String squeezeYearMonth = getSqueezeYearMonth(dateTime);
        signSqueezeBO.setDifferenceYearMonth(differenceYearMonth);
        signSqueezeBO.setSqueezeYearMonth(squeezeYearMonth);
        signSqueezeBO.setSqueezeAmount(BigDecimal.ZERO);
        signSqueezeBO.setIsSqueeze(isSqueeze);
        return signSqueezeBO;
    }

    /**
     * 获取实际签约数据相关参数
     * @param ssdBOList 签约供货详情
     * @param versionType 版本类型
     * @param projectFid 分期id
     * @return
     */
    @Override
    public SignActualCalculateBO  getSignActualCalculateBO(List<SignSupplyDetailBO> ssdBOList, String versionAttr, String projectFid) {
        SignActualCalculateBO signActualCalculateBO = new SignActualCalculateBO();

        //分期取数系统
        String countingSystemId = null;
        //查询取数系统数据
        List<NosTradedataDto> nosSigningList = new ArrayList<>();
        List<MymContractDto> mymSignList = new ArrayList<>();

        if(VersionAttrEnum.AFTER_WARPLAN.getKey().equals(versionAttr)){
            //查询该产品构成下全部楼栋id
            List<String> buildingList = ssdBOList.stream().map(SignSupplyDetailBO::getBuildId).collect(toList());
            //去重
            buildingList = buildingList.stream().distinct().collect(toList());
            //分期取数系统
            SysProjectFetchNumber sysProjectFetchNumber = sysProjectFetchNumberService.selectByFid(projectFid);
            countingSystemId = sysProjectFetchNumber.getCountingSystemId();

            if (countingSystemId.equals(FetchNnumberEnum.NOS.getKey())) {
                //查询指定楼栋中全部nos签约数据
                nosSigningList = dmOdsNosTradedataviewformarsService.selectNosContractList(buildingList);

            } else if (countingSystemId.equals(FetchNnumberEnum.MY.getKey())) {
                //查询指定楼栋中全部明源签约数据
                mymSignList = dmOdsMyVsMarsSContractService.selectMymContractList(buildingList);
            }
        }

        signActualCalculateBO.setSsdBOList(ssdBOList);
        signActualCalculateBO.setCountingSystemId(countingSystemId);
        signActualCalculateBO.setNosSigningList(nosSigningList);
        signActualCalculateBO.setMymSignList(mymSignList);
        signActualCalculateBO.setVersionAttr(versionAttr);
        return signActualCalculateBO;
    }

    //取供货计划日期、供货实际日期、虚拟供货日期中最早的日期
    private LocalDateTime getProductEarliestTime(List<SignPlanVersionSupplyDetail> supplyDetailList) {
        LocalDateTime now = LocalDateTime.now();
        //分期组团下产品构成的最早供货计划日期
        LocalDateTime earliestSupplyDate = null;
        //分期组团下产品构成的最早供货实际日期
        LocalDateTime earliestSupplyActualDate = null;
        //分期组团下产品构成的最早虚拟供货日期
        LocalDateTime earliestSupplyTimeVirtual = null;
        List<LocalDateTime> planDateList = supplyDetailList.stream().filter(s -> s.getSupplyDate() != null)
                .map(s -> s.getSupplyDate()).collect(Collectors.toList());
        List<LocalDateTime> actualDateList = supplyDetailList.stream().filter(s -> s.getSupplyDateActual() != null)
                .map(s -> s.getSupplyDateActual()).collect(Collectors.toList());
        List<LocalDateTime> timeVirtualList = supplyDetailList.stream().filter(s -> s.getSupplyTimeVirtual() != null)
                .map(s -> s.getSupplyTimeVirtual()).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(planDateList)){
            earliestSupplyDate = planDateList.stream().sorted().collect(Collectors.toList()).get(0);
        }
        if(CollectionUtils.isNotEmpty(actualDateList)){
            earliestSupplyActualDate = actualDateList.stream().sorted().collect(Collectors.toList()).get(0);
        }
        if(CollectionUtils.isNotEmpty(timeVirtualList)){
            earliestSupplyTimeVirtual = timeVirtualList.stream().sorted().collect(Collectors.toList()).get(0);
        }


        // 取供货计划日期、供货实际日期、虚拟供货日期、签约实际日期中最早的日期
        if(earliestSupplyDate != null){
            if (earliestSupplyDate.isAfter(now)) {
                earliestSupplyDate = now;
            }
            if (earliestSupplyActualDate != null) {
                if (earliestSupplyDate.isAfter(earliestSupplyActualDate)) {
                    earliestSupplyDate = earliestSupplyActualDate;
                }
            }
            if (earliestSupplyTimeVirtual != null) {
                if (earliestSupplyDate.isAfter(earliestSupplyTimeVirtual)) {
                    earliestSupplyDate = earliestSupplyTimeVirtual;
                }
            }
        }

        return earliestSupplyDate;
    }

    /**
     * 初始化本月新供签约数据
     * @param signInitializeBO 签约计划初始化参数
     * @param signActualCalculateBO 签约实际计算参数
     */
    @Override
    public void initializeEarlySignDetail(SignInitializeBO signInitializeBO, SignActualCalculateBO signActualCalculateBO) {
        SignPlanVersionDetail newSignDetail = signInitializeBO.getSignPlanVersionDetail();
        Integer isParking = signInitializeBO.getIsParking();
        boolean isBefore = signInitializeBO.getIsBefore();
        String versionType = signInitializeBO.getVersionType();
        LocalDateTime time = newSignDetail.getSignPlanTime();
        String countingSystemId = signActualCalculateBO.getCountingSystemId();
        List<MymContractDto> mymSignList = signActualCalculateBO.getMymSignList();
        List<NosTradedataDto> nosSigningList = signActualCalculateBO.getNosSigningList();
        List<SignSupplyDetailBO> ssdBOList = signActualCalculateBO.getSsdBOList();
        String versionAttr = signActualCalculateBO.getVersionAttr();

        // 当月之前取实际签约数据
        if (isBefore && VersionAttrEnum.AFTER_WARPLAN.getKey().equals(versionAttr)) {
            if (countingSystemId.equals(FetchNnumberEnum.NOS.getKey())) {
                //////////////////////////////////////NOS年初存货实际签约//////////////////////////////////////////
                // 查询指定时间年月之前供货的楼栋列表
                List<String> beforeYearBuildingList = getBuildingListbySuppluTimeActual(
                        ssdBOList, LocalDateTimeUtils.getEarliestMonthDate(time), 0);
                //查询月初存货nos签约数据
                NosSignDto earlyNosSignDto = getNosSignList(nosSigningList, beforeYearBuildingList, time);
                //年初存货每月签约数据
                newSignDetail.setYearEalySignRoomCountActual(earlyNosSignDto.getRoomCount());
                newSignDetail.setYearEalySignRoomAreaActual(earlyNosSignDto.getRoomArea());
                newSignDetail.setYearEalySignSumPriceActual(earlyNosSignDto.getTradeMoney());
                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    newSignDetail.setYearEalySignAvgPriceActual(BigDecimalUtils.divBigDecimal(newSignDetail.getYearEalySignSumPriceActual(),newSignDetail.getYearEalySignRoomAreaActual(),4));
                }else {
                    newSignDetail.setYearEalySignAvgPriceActual(BigDecimalUtils.divInteger(newSignDetail.getYearEalySignSumPriceActual(),newSignDetail.getYearEalySignRoomCountActual(),4));
                }

                //////////////////////////////////////NOS月初存货实际签约//////////////////////////////////////////
                // 查询指定时间年月之前供货的楼栋列表
                List<String> beforeMonthBuildingList = getBuildingListbySuppluTimeActual(ssdBOList, time, 0);
                //查询月初存货nos签约数据
                NosSignDto yearNosSignDto = getNosSignList(nosSigningList, beforeMonthBuildingList, time);

                // <月初存货-本月签约>实际套数
                newSignDetail.setEarlySignRoomCountActual(yearNosSignDto.getRoomCount());
                // <月初存货-本月签约>实际金额
                newSignDetail.setEarlySignSumPriceActual(yearNosSignDto.getTradeMoney());
                // <月初存货>剩余可签套数=<月初存货>套数 - <月初存货-本月签约>实际套数
                newSignDetail.setEarlyCanSignRoomCount(newSignDetail.getEarlyRoomCount()
                        .subtract(BigDecimal.valueOf(newSignDetail.getEarlySignRoomCountActual())));
                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    // <月初存货-本月签约>实际均价 = <月初存货-本月签约>实际金额 / <月初存货-本月签约>实际套数
                    newSignDetail.setEarlySignAvgPriceActual(BigDecimalUtils
                            .divInteger(newSignDetail.getEarlySignSumPriceActual(),
                                    newSignDetail.getEarlySignRoomCountActual(), 4));
                } else {
                    // <月初存货-本月签约>实际面积
                    newSignDetail.setEarlySignRoomAreaActual(yearNosSignDto.getRoomArea());
                    if (null == newSignDetail.getEarlySignRoomAreaActual()) {
                        newSignDetail.setEarlySignRoomAreaActual(BigDecimal.ZERO);
                    }
                    // <月初存货-本月签约>实际均价=<月初存货-本月签约>实际金额 / <月初存货-本月签约>实际面积
                    newSignDetail.setEarlySignAvgPriceActual(
                            BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignSumPriceActual(),
                                    newSignDetail.getEarlySignRoomAreaActual(), 4));
                }

                //////////////////////////////////////NOS本月新供实际签约//////////////////////////////////////////
                // 查询指定时间年月供货的楼栋列表
                List<String> monthSupplyPlanBuilding = getBuildingListbySuppluTimeActual(ssdBOList, time, 1);
                //查询本月新供nos签约数据
                NosSignDto SupplynosSignDto = getNosSignList(nosSigningList, monthSupplyPlanBuilding, time);

                // <本月新供-本月签约>实际套数
                newSignDetail.setSupplySignRoomCountActual(SupplynosSignDto.getRoomCount());
                // <本月新供-本月签约>实际金额
                newSignDetail.setSupplySignSumPriceActual(SupplynosSignDto.getTradeMoney());

                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    // <本月新供-本月签约>实际均价 = <本月新供-本月签约>实际金额 / <本月新供-本月签约>实际套数
                    newSignDetail.setSupplySignAvgPriceActual(BigDecimalUtils.divInteger(newSignDetail.getSupplySignSumPriceActual(), newSignDetail.getSupplySignRoomCountActual(), 4));
                } else {
                    // <本月新供-本月签约>实际面积
                    newSignDetail.setSupplySignRoomAreaActual(SupplynosSignDto.getRoomArea());
                    if (null == newSignDetail.getSupplySignRoomAreaActual()) {
                        newSignDetail.setSupplySignRoomAreaActual(BigDecimal.ZERO);
                    }
                    // <本月新供-本月签约>实际均价 = <本月新供-本月签约>实际金额 / <本月新供-本月签约>实际面积
                    newSignDetail.setSupplySignAvgPriceActual(
                            BigDecimalUtils.divBigDecimal(newSignDetail.getSupplySignSumPriceActual(),
                                    newSignDetail.getSupplySignRoomAreaActual(), 4));
                }

                //判断是否存在已售数据
                signInitializeBO.setIsSignActual(isSignActual(yearNosSignDto,SupplynosSignDto));

            } else if (countingSystemId.equals(FetchNnumberEnum.MY.getKey())) {
                // 查询指定时间年月之前供货的楼栋列表
                List<String> beforeYearBuildingList = getBuildingListbySuppluTimeActual(ssdBOList, LocalDateTimeUtils.getEarliestMonthDate(time), 0);
                // <年初存货-本月签约>实际签约数据
                ContractDto YearSignPriceActual = getMymSignList(mymSignList, beforeYearBuildingList, time);
                newSignDetail.setYearEalySignSumPriceActual(
                        YearSignPriceActual.getCjRmbTotal().add(YearSignPriceActual.getBcTotal()));

                newSignDetail.setYearEalySignRoomCountActual(YearSignPriceActual.getNum());
                newSignDetail.setYearEalySignRoomAreaActual(YearSignPriceActual.getBldArea().add(YearSignPriceActual.getBcArea()));
                newSignDetail.setYearEalySignSumPriceActual(YearSignPriceActual.getCjRmbTotal().add(YearSignPriceActual.getBcTotal()));
                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    newSignDetail.setYearEalySignAvgPriceActual(BigDecimalUtils.divBigDecimal(newSignDetail.getYearEalySignSumPriceActual(),newSignDetail.getYearEalySignRoomAreaActual(),4));
                }else {
                    newSignDetail.setYearEalySignAvgPriceActual(BigDecimalUtils.divInteger(newSignDetail.getYearEalySignSumPriceActual(),newSignDetail.getYearEalySignRoomCountActual(),4));
                }
                //////////////////////////////////////明源月初存货实际签约//////////////////////////////////////////
                // 查询当月之前供货的楼栋列表
                List<String> beforeMonthBuildingList = getBuildingListbySuppluTimeActual(ssdBOList, time, 0);
                // <月初存货-本月签约>实际签约数据
                ContractDto earlySignActualDto = getMymSignList(mymSignList, beforeMonthBuildingList, time);

                // <月初存货-本月签约>实际套数
                newSignDetail.setEarlySignRoomCountActual(earlySignActualDto.getNum());
                // <月初存货-本月签约>实际金额
                newSignDetail.setEarlySignSumPriceActual(
                        earlySignActualDto.getCjRmbTotal().add(earlySignActualDto.getBcTotal()));
                // <月初存货>剩余可签套数=<月初存货>套数 - <月初存货-本月签约>实际套数
                newSignDetail.setEarlyCanSignRoomCount(newSignDetail.getEarlyRoomCount().subtract(BigDecimal.valueOf(newSignDetail.getEarlySignRoomCountActual())));
                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    // <月初存货-本月签约>实际均价 = <月初存货-本月签约>实际金额 / <月初存货-本月签约>实际套数
                    newSignDetail.setEarlySignAvgPriceActual(BigDecimalUtils.divInteger(newSignDetail.getEarlySignSumPriceActual(), newSignDetail.getEarlySignRoomCountActual(), 4));
                } else {
                    // <月初存货-本月签约>实际面积
                    newSignDetail.setEarlySignRoomAreaActual(
                            earlySignActualDto.getBcArea().add(earlySignActualDto.getBldArea()));
                    if (null == newSignDetail.getEarlySignRoomAreaActual()) {
                        newSignDetail.setEarlySignRoomAreaActual(BigDecimal.ZERO);
                    }

                    // <月初存货-本月签约>实际均价=<月初存货-本月签约>实际金额 / <月初存货-本月签约>实际面积
                    newSignDetail.setEarlySignAvgPriceActual(
                            BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignSumPriceActual(),
                                    newSignDetail.getEarlySignRoomAreaActual(), 4));
                }

                //////////////////////////////////////明源本月新供实际签约//////////////////////////////////////////
                // 查询当月供货的楼栋列表
                List<String> monthSupplyPlanBuilding = getBuildingListbySuppluTimeActual(ssdBOList, time, 1);
                // <本月新供-本月签约>实际签约数据
                ContractDto supplySignActualDto = getMymSignList(mymSignList, monthSupplyPlanBuilding, time);

                // <本月新供-本月签约>实际套数
                newSignDetail.setSupplySignRoomCountActual(supplySignActualDto.getNum());
                // <本月新供-本月签约>实际金额
                newSignDetail.setSupplySignSumPriceActual(supplySignActualDto.getCjRmbTotal().add(supplySignActualDto.getBcTotal()));

                if (isParking.equals(WhetherEnum.YES.getKey())) {
                    // <本月新供-本月签约>实际均价 = <本月新供-本月签约>实际金额 / <本月新供-本月签约>实际套数
                    newSignDetail.setSupplySignAvgPriceActual(BigDecimalUtils.divInteger(newSignDetail.getSupplySignSumPriceActual(), newSignDetail.getSupplySignRoomCountActual(), 4));
                } else {
                    // <本月新供-本月签约>实际面积
                    newSignDetail.setSupplySignRoomAreaActual(supplySignActualDto.getBcArea().add(supplySignActualDto.getBldArea()));
                    if (null == newSignDetail.getSupplySignRoomAreaActual()) {
                        newSignDetail.setSupplySignRoomAreaActual(BigDecimal.ZERO);
                    }

                    // <本月新供-本月签约>实际均价 = <本月新供-本月签约>实际金额 / <本月新供-本月签约>实际面积
                    newSignDetail.setSupplySignAvgPriceActual(BigDecimalUtils.divBigDecimal(newSignDetail.getSupplySignSumPriceActual(), newSignDetail.getSupplySignRoomAreaActual(), 4));
                }

                //判断是否存在已售数据
                signInitializeBO.setIsSignActual(isSignActual(earlySignActualDto,supplySignActualDto));
            }
        }
    }

    //判断是否存在已售数据
    private boolean isSignActual(NosSignDto dtoA,NosSignDto dtoB) {
        BigDecimal zero = BigDecimal.ZERO;
        if(dtoA.getRoomCount().intValue() != 0 || dtoA.getRoomArea().compareTo(zero) != 0 || dtoA.getTradeMoney().compareTo(zero) != 0){
            return true;
        }
        if(dtoB.getRoomCount().intValue() != 0 || dtoB.getRoomArea().compareTo(zero) != 0 || dtoB.getTradeMoney().compareTo(zero) != 0){
            return true;
        }
        return false;
    }

    //判断是否存在已售数据
    private boolean isSignActual(ContractDto dtoA,ContractDto dtoB) {
        BigDecimal zero = BigDecimal.ZERO;
        if(dtoA.getNum().intValue() != 0 || dtoA.getCjRmbTotal().compareTo(zero) != 0 || dtoA.getBldArea().compareTo(zero) != 0 || dtoA.getBcTotal().compareTo(zero) != 0 || dtoA.getBcArea().compareTo(zero) != 0){
            return true;
        }
        if(dtoB.getNum().intValue() != 0 || dtoB.getCjRmbTotal().compareTo(zero) != 0 || dtoB.getBldArea().compareTo(zero) != 0 || dtoB.getBcTotal().compareTo(zero) != 0 || dtoB.getBcArea().compareTo(zero) != 0){
            return true;
        }
        return false;
    }

    /**
     * 初始化本月新供签约数据
     * @param signInitializeBO 签约计划初始化参数
     */
    private void initializeSupplySignDetail(SignInitializeBO signInitializeBO) {
        SignPlanVersionDetail newSignDetail = signInitializeBO.getSignPlanVersionDetail();
        List<SignSupplyDetailBO> signSupplyDetailList = signInitializeBO.getSignSupplyDetailList();
        Integer isParking = signInitializeBO.getIsParking();
        boolean isBefore = signInitializeBO.getIsBefore();
        String versionType = signInitializeBO.getVersionType();

        // 是否车位楼栋
        if (isParking.equals(WhetherEnum.YES.getKey())) {
            if (CollectionUtils.isNotEmpty(signSupplyDetailList)) {
                for (SignSupplyDetailBO supplyDetail : signSupplyDetailList) {
                    // <本月新供>车位数
                    newSignDetail.setSupplyRoomCount(newSignDetail.getSupplyRoomCount().add(BigDecimal.valueOf(supplyDetail.getParkingNum())));
                    // <本月新供>金额
                    newSignDetail.setSupplySumPrice(newSignDetail.getSupplySumPrice().add(supplyDetail.getNotCommodityValue()));
                }
                // <本月新供>均价 = <本月新供>金额 / <本月新供>套数
                newSignDetail.setSupplyAvgPrice(BigDecimalUtils.divBigDecimal(newSignDetail.getSupplySumPrice(), newSignDetail.getSupplyRoomCount(), 4));
            }
        } else {
            if (CollectionUtils.isNotEmpty(signSupplyDetailList)) {
                for (SignSupplyDetailBO supplyDetail : signSupplyDetailList) {
                    // <本月新供>套数
                    newSignDetail.setSupplyRoomCount(
                            newSignDetail.getSupplyRoomCount().add(BigDecimal.valueOf(supplyDetail.getRoomNum())));
                    // <本月新供>面积
                    newSignDetail.setSupplyRoomArea(newSignDetail.getSupplyRoomArea().add(supplyDetail.getSaleArea()));
                    if (null == newSignDetail.getSupplyRoomArea()) {
                        newSignDetail.setSupplyRoomArea(BigDecimal.ZERO);
                    }
                    // <本月新供>金额
                    newSignDetail.setSupplySumPrice(newSignDetail.getSupplySumPrice().add(supplyDetail.getHouseCommodityValue()));
                }
                // <本月新供>套均面积 = <本月新供>面积 / <本月新供>套数
                newSignDetail.setSupplyAvgRoomArea(BigDecimalUtils
                        .divBigDecimal(newSignDetail.getSupplyRoomArea(), newSignDetail.getSupplyRoomCount(), 4));
                // <本月新供>均价 = <本月新供>金额 / <本月新供>面积
                newSignDetail.setSupplyAvgPrice(BigDecimalUtils.divBigDecimal(
                        newSignDetail.getSupplySumPrice(), newSignDetail.getSupplyRoomArea(), 4));
            }
        }
    }

    /**
     * 复制上一版签约计划
     * @param SignInitializeBO 签约计划初始化参数
     * @param oldSpvdList 上一版签约计划的签约详情数据集合
     * @param signSqueezeBO 签约计划倒挤参数
     */
    private void copyOldVersionSignPlan(SignInitializeBO signInitializeBO, List<SignPlanVersionDetail> oldSpvdList, SignSqueezeBO signSqueezeBO) {
        if(CollectionUtils.isEmpty(oldSpvdList)){
            return;
        }
        SignPlanVersionDetail newSignDetail = signInitializeBO.getSignPlanVersionDetail();
        Integer isParking = signInitializeBO.getIsParking();
        String versionType = signInitializeBO.getVersionType();
        String yearMonth = signInitializeBO.getYearMonth();
        boolean isBefore = signInitializeBO.getIsBefore();

        String differenceYearMonth = signSqueezeBO.getDifferenceYearMonth();
        String squeezeYearMonth = signSqueezeBO.getSqueezeYearMonth();
        boolean isSqueeze = signSqueezeBO.getIsSqueeze();

        List<SignPlanVersionDetail> signPlanDetails = oldSpvdList.stream().filter(s -> yearMonth.equals(LocalDateTimeUtils.getLocalMonth(s.getSignPlanTime()))).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(signPlanDetails)) {
            SignPlanVersionDetail signPlanDetail = signPlanDetails.get(0);
            // <月初存货-本月签约>计划金额=上个版本<月初存货-本月签约>计划金额
            newSignDetail.setEarlySignSumPrice(signPlanDetail.getEarlySignSumPrice());
            if(newSignDetail.getEarlySignSumPrice().compareTo(BigDecimal.ZERO)==0){
                newSignDetail.setEarlySignAvgPrice(BigDecimal.ZERO);
            }else {
                // <月初存货-本月签约>计划均价=月初存货>均价
                newSignDetail.setEarlySignAvgPrice(newSignDetail.getEarlyAvgPrice());
            }
            if (isParking == WhetherEnum.YES.getKey()) {
                // <月初存货-本月签约>计划车位数 = <月初存货-本月签约>计划金额 / <月初存货-本月签约>均价
                newSignDetail.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignSumPrice(),newSignDetail.getEarlySignAvgPrice(),5));
            }else {
                // <月初存货-本月签约>计划面积 = <月初存货-本月签约>计划金额 / <月初存货-本月签约>均价
                newSignDetail.setEarlySignRoomArea(BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignSumPrice(),newSignDetail.getEarlySignAvgPrice(),4));
                // <月初存货-本月签约>计划套数 = <月初存货-本月签约>计划面积 / <月初存货>套均面积
                newSignDetail.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignRoomArea(),newSignDetail.getEarlyAvgRoomArea(),5));
            }

            // <本月新供-本月签约>金额=上个版本<本月新供-本月签约>金额
            newSignDetail.setSupplySignSumPrice(signPlanDetail.getSupplySignSumPrice());
            if(newSignDetail.getSupplySignSumPrice().compareTo(BigDecimal.ZERO) == 0){
                // <本月新供-本月签约>计划均价=<本月新供>均价
                newSignDetail.setSupplySignAvgPrice(BigDecimal.ZERO);
            }else {
                // <本月新供-本月签约>计划均价=<本月新供>均价
                newSignDetail.setSupplySignAvgPrice(newSignDetail.getSupplyAvgPrice());
            }
            if (isParking == WhetherEnum.YES.getKey()) {
                // <本月新供-本月签约>计划车位数 = <本月新供-本月签约>计划金额 / <本月新供-本月签约>均价
                newSignDetail.setSupplySignRoomCount(BigDecimalUtils.divBigDecimal(newSignDetail.getSupplySignSumPrice(),newSignDetail.getSupplySignAvgPrice(),5));
            }else {
                // <本月新供-本月签约>计划面积 = <本月新供-本月签约>计划金额 / <本月新供-本月签约>均价
                newSignDetail.setSupplySignRoomArea(BigDecimalUtils.divBigDecimal(newSignDetail.getSupplySignSumPrice(),newSignDetail.getSupplySignAvgPrice(),4));
                // <本月新供-本月签约>计划套数 = <本月新供-本月签约>计划面积 / <本月新供>套均面积
                newSignDetail.setSupplySignRoomCount(BigDecimalUtils.divBigDecimal(newSignDetail.getSupplySignRoomArea(),newSignDetail.getSupplyAvgRoomArea(),5));
            }

        }
        //复制上月的签约计划，对上个月签约实际金额与签约计划金额的差异做倒挤操作
        if(isSqueeze){
            //计算差异年月的差异金额
            if(differenceYearMonth.equals(yearMonth)){
                //<月初存货-本月签约>实际金额 - <月初存货-本月签约>计划金额 + <本月新供-本月签约>实际金额 - <本月新供-本月签约>计划金额
                BigDecimal earlySignSumPriceActual = newSignDetail.getEarlySignSumPriceActual();
                BigDecimal earlySignSumPrice = newSignDetail.getEarlySignSumPrice();
                BigDecimal supplySignSumPriceActual = newSignDetail.getSupplySignSumPriceActual();
                BigDecimal supplySignSumPrice = newSignDetail.getSupplySignSumPrice();
                BigDecimal squeezeAmount = earlySignSumPriceActual.subtract(earlySignSumPrice).add(supplySignSumPriceActual).subtract(supplySignSumPrice);
                signSqueezeBO.setSqueezeAmount(squeezeAmount);
            }
            //倒挤到倒挤月的月初存货签约计划金额上
            if(squeezeYearMonth.equals(yearMonth)){
                // <月初存货-本月签约>计划金额
                BigDecimal earlySignSumPrice = newSignDetail.getEarlySignSumPrice();
                // 差异值
                BigDecimal squeezeAmount = signSqueezeBO.getSqueezeAmount();
                // <月初存货-本月签约>计划金额 = <月初存货-本月签约>计划金额 - 差异值
                newSignDetail.setEarlySignSumPrice(earlySignSumPrice.subtract(squeezeAmount));
                //重新计算月初存货签约数据
                if(newSignDetail.getEarlySignSumPrice().compareTo(BigDecimal.ZERO)==0){
                    newSignDetail.setEarlySignAvgPrice(BigDecimal.ZERO);
                }else {
                    // <月初存货-本月签约>计划均价=月初存货>均价
                    newSignDetail.setEarlySignAvgPrice(newSignDetail.getEarlyAvgPrice());
                }
                if (isParking == WhetherEnum.YES.getKey()) {
                    // <月初存货-本月签约>计划车位数 = <月初存货-本月签约>计划金额 / <月初存货-本月签约>均价
                    newSignDetail.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignSumPrice(),newSignDetail.getEarlySignAvgPrice(),5));
                }else {
                    // <月初存货-本月签约>计划面积 = <月初存货-本月签约>计划金额 / <月初存货-本月签约>均价
                    newSignDetail.setEarlySignRoomArea(BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignSumPrice(),newSignDetail.getEarlySignAvgPrice(),4));
                    // <月初存货-本月签约>计划套数 = <月初存货-本月签约>计划面积 / <月初存货>套均面积
                    newSignDetail.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(newSignDetail.getEarlySignRoomArea(),newSignDetail.getEarlyAvgRoomArea(),5));
                }
                //重置差异值
                signSqueezeBO.setSqueezeAmount(BigDecimal.ZERO);
            }
        }
    }

    /**
     * 签约供货详情列表转换
     * 有实际取实际，无实getMymSignList际取计划
     * @param spvsdList 签约供货详情列表
     * @return
     */
    @Override
    public List<SignSupplyDetailBO> switchSignSupplyDetailBO(List<SignPlanVersionSupplyDetail> spvsdList) {
        List<SignSupplyDetailBO> ssdBOList = new ArrayList<>();

        for (SignPlanVersionSupplyDetail spvsd : spvsdList) {
            SignSupplyDetailBO ssdBO = new SignSupplyDetailBO();
            ssdBO.setIsParking(spvsd.getIsParking());
            ssdBO.setBuildId(spvsd.getOrgId());
            ssdBO.setId(spvsd.getId());
            if (spvsd.getSupplyDateActual() != null || spvsd.getSupplyTimeVirtual() != null) {
                ssdBO.setRoomNum(spvsd.getRoomNumActual());
                ssdBO.setParkingNum(spvsd.getParkingNumActual());
                ssdBO.setSaleArea(spvsd.getAreaActual());
                ssdBO.setAveragePriceHouse(spvsd.getAveragePriceHouseActual());
                ssdBO.setAveragePriceParking(spvsd.getAveragePriceParkingActual());
                ssdBO.setHouseCommodityValue(spvsd.getHouseCommodityValueActual());
                ssdBO.setNotCommodityValue(spvsd.getNotCommodityValueActual());
                ssdBO.setSupplyDate(spvsd.getSupplyTimeVirtual() != null ? spvsd.getSupplyTimeVirtual() : spvsd.getSupplyDateActual());
                ssdBO.setIsActual(1);
            } else {
                ssdBO.setRoomNum(spvsd.getRoomNum());
                ssdBO.setParkingNum(spvsd.getParkingNum());
                ssdBO.setSaleArea(spvsd.getSaleArea());
                ssdBO.setAveragePriceHouse(spvsd.getAveragePriceHouse());
                ssdBO.setAveragePriceParking(spvsd.getAveragePriceParking());
                ssdBO.setHouseCommodityValue(spvsd.getHouseCommodityValue());
                ssdBO.setNotCommodityValue(spvsd.getNotCommodityValue());
                ssdBO.setSupplyDate(spvsd.getSupplyDate());
                ssdBO.setIsActual(0);
            }
            ssdBOList.add(ssdBO);
        }
        return ssdBOList;
    }

    /**
     * 获取指定时间的楼栋列表
     *
     * @param ssdBOList 签约供货详情列表
     * @param time      指定时间
     * @param type      查询类型（0-小于指定年月,1-等于指定年月））
     * @return
     */
    @Override
    public List<String> getBuildingListbySuppluTimeActual(List<SignSupplyDetailBO> ssdBOList, LocalDateTime time, int type) {
        List<String> buildings = new ArrayList<>();
        int yearMonth = LocalDateTimeUtils.toIntYearMonth(time.getYear(), time.getMonthValue());
        if (type == 0) {
            for (SignSupplyDetailBO ssdBO : ssdBOList) {
                int supplyDateActual = LocalDateTimeUtils.toIntYearMonth(ssdBO.getSupplyDate().getYear(), ssdBO.getSupplyDate().getMonthValue());
                if (supplyDateActual < yearMonth) {
                    buildings.add(ssdBO.getBuildId());
                }
            }
        } else if (type == 1) {
            for (SignSupplyDetailBO ssdBO : ssdBOList) {
                int supplyDateActual = LocalDateTimeUtils.toIntYearMonth(ssdBO.getSupplyDate().getYear(), ssdBO.getSupplyDate().getMonthValue());
                if (supplyDateActual == yearMonth) {
                    buildings.add(ssdBO.getBuildId());
                }
            }
        }
        return buildings;
    }

    /**
     * 获取指定时间中的签约供货详情
     *
     * @param spvsdList 签约供货详情列表
     * @param yearMonth （年-月）字符串
     * @return
     */
    @Override
    public List<SignSupplyDetailBO> getSignPlanVersionSupplyDetailBySuppluTime(List<SignSupplyDetailBO> spvsdList, String yearMonth) {
        List<SignSupplyDetailBO> signSupplyDetailBOList = new ArrayList<>();
        for (SignSupplyDetailBO supplyDetailBO : spvsdList) {
            if (LocalDateTimeUtils.getLocalMonth(supplyDetailBO.getSupplyDate()).equals(yearMonth)) {
                signSupplyDetailBOList.add(supplyDetailBO);
            }
        }
        return signSupplyDetailBOList;
    }


    /**
     * 获取结转日期
     *
     * @param versionId  价格版本id
     * @param priceOrgId 价格版本详情1id
     * @return
     */
    public LocalDateTime getTransferDate(String versionId, String priceOrgId) {

        CarryOverTimeDto dto = signPlanVersionMapper.selectPriceByProductId(versionId, priceOrgId,
                IsDeleteEnum.NO.getKey());
        LocalDateTime now = null;
        if (dto != null) {
            if (StringUtils.isNotBlank(dto.getActualTime())) {
                DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                now = LocalDateTime.parse(dto.getActualTime(), df);
            } else {
                if (StringUtils.isNotBlank(dto.getPlanTime())) {
                    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    now = LocalDateTime.parse(dto.getPlanTime(), df);
                }
            }
        }
        return now;
    }

    /**
     * 根据签约计划版本id删除签约计划版本信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public String deleteSignPlanVersion(String versionId,String alias) {
        // 只有带编制和编制中才能删除
        String id = versionId;
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(id, IsDeleteEnum.NO.getKey());
        if (signPlanVersion != null) {
            if (signPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()) {
                return "删除成功";
            }
            String versionState = signPlanVersion.getVersionState();
            if (versionState.equals(VersionStatusEnum.NOTPREPARED.getKey())) {
                // 待编制
            } else if (versionState.equals(VersionStatusEnum.ORGANIZATION.getKey())) {
                // 编制中
            } else {
                // 不能删除
                throw new MsgException(CodeEnum.NON_DELETE_VERSIONS);
            }
            signPlanVersion.setIsDelete(IsDeleteEnum.YES.getKey());

            SignPlanVersionDetail signPlanVersionDetail = new SignPlanVersionDetail();
            signPlanVersionDetail.setIsDelete(IsDeleteEnum.YES.getKey());

            SignPlanVersionSupplyDetail signPlanVersionSupplyDetail = new SignPlanVersionSupplyDetail();
            signPlanVersionSupplyDetail.setIsDelete(IsDeleteEnum.YES.getKey());
            // 删除签约明细
            UpdateWrapper<SignPlanVersionDetail> signPlanVersionDetailWrapper = new UpdateWrapper<>();
            signPlanVersionDetailWrapper.eq("version_id", versionId);
            // 删除签约供货详细信息
            UpdateWrapper<SignPlanVersionSupplyDetail> signPlanVersionSupplyDetailWrapper = new UpdateWrapper<>();
            signPlanVersionSupplyDetailWrapper.eq("version_id", versionId);
            // 删除签约计划信息
            signPlanVersionMapper.updateById(signPlanVersion);
            signPlanVersionDetailMapper.update(signPlanVersionDetail, signPlanVersionDetailWrapper);
            signPlanVersionSupplyDetailMapper.update(signPlanVersionSupplyDetail, signPlanVersionSupplyDetailWrapper);

            saveVersionLog(signPlanVersion.getId(),VersionEventEnum.MANUAL_DELETE.getKey(),signPlanVersion.getVersionState(),alias,"删除签约版本","删除签约版本");
            saveLog(LogEventEnum.DELETE,"签约计划","签约计划删除成功","versionId:"+versionId);
        }
        return "删除成功";
    }


    /**
     * 根据版本id查询签约计划版本
     *
     * @param versionId 版本id
     * @param year      指定年份
     * @return
     */
    @Override
    public SignPlanVersionVo getSignPlanVersion(String versionId, String year) {
        SignPlanVersionVo vo = new SignPlanVersionVo();
        List<SignPlanVersionDetailDto> detailList = new ArrayList<>();

        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
        if (null == signPlanVersion) {
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }


        // 版本所属时间
        String signAffiliationMonth = signPlanVersion.getAffiliationMonth();
        LocalDateTime affiliationMonth = null;
        if (signAffiliationMonth != null) {
            affiliationMonth = LocalDateTimeUtils.toLocalDateTime(signPlanVersion.getAffiliationMonth());
        }

        // 要被查询的日期（若year为空，则查询当前年）
        LocalDateTime dateTime;
        if (StringUtils.isEmpty(year)) {
            dateTime = LocalDateTimeUtils.getEarliestMonthDate(LocalDateTime.now());
        } else {
            dateTime = LocalDateTimeUtils.getEarliestMonthDate(year);
        }
        // 分期
        List<SignPlanVersionSupplyDetail> stageList = signPlanVersionSupplyDetailMapper
                .selectByType(versionId, OrgTypeEunm.ORG_STAGE.getKey(), IsDeleteEnum.NO.getKey());

        // 组团
        List<SignPlanVersionSupplyDetail> groupList = signPlanVersionSupplyDetailMapper
                .selectByType(versionId, OrgTypeEunm.ORG_GROUP.getKey(), IsDeleteEnum.NO.getKey());

        // 产品构成
        List<SignPlanVersionDetailDto> productDtoList = new ArrayList<>();
        for (SignPlanVersionSupplyDetail group : groupList) {
            String pid = group.getSupplyPlanId();
            String groupId = group.getOrgId();
            // 产品构成
            List<SignPlanVersionSupplyDetail> signPlanVersionSupplyDetails = signPlanVersionSupplyDetailMapper
                    .selectProductDetail(versionId, pid, OrgTypeEunm.ORG_PRODUCT.getKey(), null, null,
                            IsDeleteEnum.NO.getKey());

            // 计算产品构成数据
            if (signPlanVersionSupplyDetails != null) {
                // 来源供货版本id
                String sourceSupplyId = signPlanVersion.getSourceSupplyId();
                //产品构成详情表
                List<SignPlanVersionDetailDto> productDetailList = calculateProductData(signPlanVersionSupplyDetails, sourceSupplyId, dateTime, affiliationMonth, groupId);
                productDtoList.addAll(productDetailList);
            }
        }

        // 产品构成数据向组团汇总
        List<SignPlanVersionDetailDto> grouDtoList = signDataSorting(groupList, productDtoList, OrgTypeEunm.ORG_PRODUCT.getKey());
        // 组团数据向分期汇总
        List<SignPlanVersionDetailDto> stageDtoList = signDataSorting(stageList, grouDtoList, OrgTypeEunm.ORG_GROUP.getKey());

        // 组团排序
        groupOrderBy(grouDtoList,"org_group");

        detailList.addAll(productDtoList);
        detailList.addAll(grouDtoList);
        detailList.addAll(stageDtoList);

        // 单位转换
        detailList = unitConversion(detailList);

        List<String> versionIds = new ArrayList<>();
        versionIds.add(versionId);
        List<String> timeList = getDateList(versionIds);
        //若无当年年份,添加当年年份
        String yearStr = String.valueOf(LocalDateTime.now().getYear());
        List<String> years = timeList.stream().filter(s -> s.equals(yearStr)).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(years)){
            timeList.add(yearStr);
        }
        timeList.sort(Comparator.comparing(String::toString));

        vo.setSignPlanVersionId(versionId);
        vo.setDetailList(detailList);
        vo.setTimeList(timeList);
        return vo;
    }

    /**
     * 查询签约版本全路径名称与来源版本全路径名称
     */
    @Override
    public FullPathVo getSignFullPath(String versionId) {
        // 签约计划版本
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
        if (null == signPlanVersion) {
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }
        return getSignVersionFullNameVo(signPlanVersion);
    }

    /**
     * 版本列表全周期年份列表
     *
     * @param versionIds 签约版本列表
     * @return
     */
    public List<String> getDateList(List<String> versionIds) {

        // 获取版本列表中最早供货时间与最晚签约时间
        StartAndEndTimeBO startAndEndTime = getStartAndEndTime(versionIds);
        // 查询版本列表中最早供货计划日期
        LocalDateTime earliestSupplyDate = startAndEndTime.getStartDate();
        // 查询该分期最晚签约时间
        LocalDateTime latestSignDate = startAndEndTime.getEndDate();

        List<String> timeList = new ArrayList<>();
        int earliestyear = earliestSupplyDate.getYear();
        int latestyear = 0;
        if (null == latestSignDate) {
            latestyear = earliestSupplyDate.getYear();
        } else {
            latestyear = latestSignDate.getYear();
        }
        for (int year = earliestyear; year <= latestyear; year++) {
            timeList.add(String.valueOf(year));
        }
        return timeList;
    }

    /**
     * 根据签约计划版本获取签约版本全路径名称与来源版本全路径名称
     */
    private FullPathVo getSignVersionFullNameVo(SignPlanVersion signPlanVersion) {
        if(null == signPlanVersion){
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }
        FullPathVo vo = new FullPathVo();
        //签约版本id
        String signPlanVersionId = signPlanVersion.getId();
        // 签约计划版本号
        String versionNum = signPlanVersion.getVersionNum();
        // 版本状态编码
        String versionStateCode = signPlanVersion.getVersionState();
        // 版本状态名
        String versionState = VersionStatusEnum.getValueByKey(versionStateCode);
        // 签约计划版本类型
        String versionType = signPlanVersion.getVersionType();
        // 签约计划版本类型名称
        String versionTypeName = versionType.equals(VersionTypeEunm.BUDGET.getKey()) ? signPlanVersion.getVersionIdentify() : VersionTypeEunm.getValueByKey(versionType);
        // 签约计划归属月份
        String signAffiliationMonth = signPlanVersion.getAffiliationMonth();
        String affiliationMonth = null == signAffiliationMonth
                ? null : signAffiliationMonth.replaceAll("-", "");
        // 签约计划预算版版本标签
        String versionIdentify = signPlanVersion.getVersionIdentify();
        
        // 是否考核版
        String isAssessmentVersion = String.valueOf(signPlanVersion.getIsAssessmentVersion() == null
                ? 0 : signPlanVersion.getIsAssessmentVersion());

        //版本创建人
        String createUser = "";
        if(signPlanVersion.getCreateBy().equals(Constants.SYS)) {
        	createUser = Constants.SYS_NAME;
        }else {
        	IdmUser user = idmUserService.getByAliasLimitOne(signPlanVersion.getCreateBy());
        	if(user != null) {
        		createUser = user.getUsercn();
        	}
        }
        //版本创建时间
        LocalDateTime createTime = signPlanVersion.getCreateTime();
        //版本修改时间
        LocalDateTime updateTime = signPlanVersion.getUpdateTime();
        //版本创建方式编码
        String createMethodCode = signPlanVersion.getVersionCreate();
        //版本创建方式名称
        String createMethodName = VersionCreateEnum.getKeyByValue(createMethodCode);
        
        if(versionType.equals(VersionTypeEunm.BUDGET.getKey()) && StrUtils.isNotEmpty(signPlanVersion.getSourceSignId())) {
        	 // 供货版本id
            String sourceVersionId = signPlanVersion.getSourceSignId();
            // 供货计划版本
            SignPlanVersion sourceSignPlanVersion = signPlanVersionMapper.selectById(sourceVersionId);
            if (null == sourceSignPlanVersion) {
                throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
            }
            // 签约版本号
            String sourceVersionNum = sourceSignPlanVersion.getVersionNum();
            // 签约版本类型
            String sourceVersionTypeCode = sourceSignPlanVersion.getVersionType();
            // 签约版本类型名称
            String sourceVersionTypeName = VersionTypeEunm.getValueByKey(sourceVersionTypeCode);
            // 签约版本预算版版本属性
            String sourceVersionIdentify = sourceSignPlanVersion.getVersionIdentify();
            // 签约版本创建时间
            LocalDateTime sourceCreateTime = sourceSignPlanVersion.getCreateTime();
            // 签约版本修改时间
            LocalDateTime sourceUpdateTime = sourceSignPlanVersion.getUpdateTime();
            // 签约版本归属月份
            String sourceAffiliationMonth = null == sourceSignPlanVersion.getAffiliationMonth()
                    ? null : sourceSignPlanVersion.getAffiliationMonth().replaceAll("-", "");
            
            if(StrUtils.isNotEmpty(sourceVersionIdentify)) {
            	if(sourceVersionIdentify.equals(VersionIdentifyEnum.ALL_YEAR.getKey()) || sourceVersionIdentify.equals(VersionIdentifyEnum.MID_YEAR.getKey())){
            		sourceVersionIdentify = sourceSignPlanVersion.getCompileYear()+  VersionIdentifyEnum.getValueByKey(sourceVersionIdentify) ;       	
            	}
            }
            
            vo.setSourceVersionId(sourceVersionId);
            vo.setSourceVersionNum(sourceVersionNum);
            vo.setSourceVersionTypeCode(sourceVersionTypeCode);
            vo.setSourceVersionTypeName(sourceVersionTypeName);
            vo.setSourceAffiliationMonth(sourceAffiliationMonth);
            vo.setSourceVersionIdentify(sourceVersionIdentify);
            vo.setSourceCreateTime(sourceCreateTime);
            vo.setSourceUpdateTime(sourceUpdateTime);
            vo.setSourceIsAssessmentVersion("0");
        }else {
        	// 供货版本id
            String sourceVersionId = signPlanVersion.getSourceSupplyId();
            // 供货计划版本
            SupplyPlanVersion supplyPlanVersion = supplyPlanVersionMapper.selectById(sourceVersionId);
            if (null == supplyPlanVersion) {
                throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
            }
            // 供货版本号
            String sourceVersionNum = supplyPlanVersion.getVersionNum();
            // 供货版本类型
            String sourceVersionTypeCode = supplyPlanVersion.getVersionType();
            // 供货版本类型名称
            String sourceVersionTypeName = VersionTypeEunm.getValueByKey(sourceVersionTypeCode);
            // 供货版本预算版版本属性
            String sourceVersionIdentify = supplyPlanVersion.getVersionIdentify();
            // 供货版本创建时间
            LocalDateTime sourceCreateTime = supplyPlanVersion.getCreateTime();
            // 供货版本修改时间
            LocalDateTime sourceUpdateTime = supplyPlanVersion.getUpdateTime();
            // 供货版本归属月份
            String sourceAffiliationMonth = null == supplyPlanVersion.getAffiliationMonth()
                    ? null : supplyPlanVersion.getAffiliationMonth().replaceAll("-", "");

            String sourceIsAssessmentVersion = "0";
            if(supplyPlanVersion.getVersionType().equals(VersionTypeEunm.INVESTMENT.getKey())){
                sourceIsAssessmentVersion = String.valueOf(supplyPlanVersion.getIsAssessmentVersion() == null
                        ? 0 : supplyPlanVersion.getIsAssessmentVersion().toString());
            }
            
            vo.setSourceVersionId(sourceVersionId);
            vo.setSourceVersionNum(sourceVersionNum);
            vo.setSourceVersionTypeCode(sourceVersionTypeCode);
            vo.setSourceVersionTypeName(sourceVersionTypeName);
            vo.setSourceAffiliationMonth(sourceAffiliationMonth);
            vo.setSourceVersionIdentify(sourceVersionIdentify);
            vo.setSourceCreateTime(sourceCreateTime);
            vo.setSourceUpdateTime(sourceUpdateTime);
            vo.setSourceIsAssessmentVersion(sourceIsAssessmentVersion);
        }
        

        vo.setVersionId(signPlanVersionId);
        vo.setVersionNum(versionNum);
        vo.setVersionState(versionState);
        vo.setVersionStateCode(versionStateCode);
        vo.setVersionType(versionType);
        vo.setVersionTypeName(versionTypeName);
        vo.setAffiliationMonth(affiliationMonth);
        vo.setVersionIdentify(versionIdentify);
        vo.setIsAssessmentVersion(isAssessmentVersion);
        vo.setCreateUser(createUser);
        vo.setCreateTime(createTime);
        vo.setUpdateTime(updateTime);
        vo.setCreateMethodCode(createMethodCode);
        vo.setCreateMethodName(createMethodName);
       
        return vo;
    }

    /**
     * 保存组合业态签约计划
     * @param signPlanDetailParam
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public String saveProductSignPlan(SignPlanDetailParam signPlanDetailParam, String alias) {
        // 签约版本id
        String versionId = signPlanDetailParam.getVersionId();
        // 签约计划详情id
        String signPlanVersionId = signPlanDetailParam.getSignPlanVersionId();
        // 签约计划详情列表
        List<SignPlanDetailDto> spddList = signPlanDetailParam.getSignPlanDetailList();
        //查询签约计划版本
        SignPlanVersion signPlanVersion;
        //签约计划详情
        SignPlanVersionDetail signPlanVersionDetail;
        // 产品构成属性类别
        SignProductCodeBO signProductCodeBO = new SignProductCodeBO();

        if (StringUtils.isEmpty(versionId) || StringUtils.isEmpty(signPlanVersionId) || CollectionUtils.isEmpty(spddList) ) {
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //查询签约计划版本
        signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
        if (null == signPlanVersion) {
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }
        //查询签约计划详情
        signPlanVersionDetail = signPlanVersionDetailMapper.selectById(signPlanVersionId);
        if (null == signPlanVersionDetail) {
            throw new MsgException(CodeEnum.SIGN_PLAN_VERSION_SUPPLY_DETAIL_ERROR);
        }

        //赋值产品构成组合业态
        signProductCodeBO.setVersionId(versionId);
        signProductCodeBO.setPid(signPlanVersionDetail.getGroupId());
        signProductCodeBO.setProductCode(signPlanVersionDetail.getProductCode());
        signProductCodeBO.setBusinessType(signPlanVersionDetail.getBusinessType());
        signProductCodeBO.setFreeType(signPlanVersionDetail.getFreeType());
        signProductCodeBO.setProType(signPlanVersionDetail.getProType());
        signProductCodeBO.setIsDel(IsDeleteEnum.YES.getKey());
        signProductCodeBO.setTime(signPlanVersion.getAffiliationMonth());
        // 版本处于审批中或已经审批通过,不能保存
        if(signPlanVersion.getVersionState().equals(VersionStatusEnum.APPROVED.getKey())
                || signPlanVersion.getVersionState().equals(VersionStatusEnum.SUCCESSAPPROVED.getKey())){
            throw new MsgException(CodeEnum.APPROVED_OR_SUCCESSAPPROVED_SAVE_ERROR);
        }

        // 判断是否编制完成，做补差处理
        int isEstablishment = signPlanDetailCompensation(spddList,signPlanVersion);

        //版本在待提交审批、审批被驳回状态中,版本未全部编制完成,不允许保存
        if (signPlanVersion.getVersionState().equals(VersionStatusEnum.EXAMINEDAPPROVED.getKey())
                || signPlanVersion.getVersionState().equals(VersionStatusEnum.FAILUREAPPROVED.getKey())) {
            if (isEstablishment == WhetherEnum.NO.getKey()) {
                throw new MsgException(CodeEnum.NOT_ESTABLISHMENTNUM_ERROR);
            }
        }

        // 删除签约计划详情
        signPlanVersionDetailMapper.updateProduct(signProductCodeBO);
        // 获取新增月份的签约计划
        List<SignPlanVersionDetail> newSignPlanDetailList = getNewSignPlanDetailList(signPlanVersionDetail,spddList,signPlanVersion.getAffiliationMonth(),alias);
        // 保存新增月份的签约计划
        if (CollectionUtils.isNotEmpty(newSignPlanDetailList)) {
            signPlanVersionDetailMapper.insertList(newSignPlanDetailList);
        }

        // 修改版本状态
        String afterState = signPlanVersion.getVersionState();
        // 编制中、待提交审批、审批已驳回状态的版本编辑后不修改版本状态
        if (signPlanVersion.getVersionState().equals(VersionStatusEnum.NOTPREPARED.getKey())) {
            afterState = VersionStatusEnum.ORGANIZATION.getKey();
        }
        signPlanVersion.setVersionState(afterState);
        signPlanVersion.setUpdateTime(LocalDateTime.now());
        signPlanVersionMapper.updateById(signPlanVersion);
        saveVersionLog(versionId,VersionEventEnum.UPDATE_SAVE.getKey(),signPlanVersion.getVersionState(),alias,"保存签约计划",null);

        //验证签约与供货是否一致
        if(isEstablishment == 1){
            LocalDateTime time = LocalDateTimeUtils.toLocalDateTime(signPlanVersion.getAffiliationMonth());
            Integer isParking = signPlanVersionDetail.getIsParking();
            isEstablishment = checkSupplyAndSign(signProductCodeBO,time,isParking);
        }

        // 修改该组合业态签约详情的编制状态
        signProductCodeBO.setIsDel(WhetherEnum.NO.getKey());
        signProductCodeBO.setIsEstablishment(isEstablishment);
        signPlanVersionDetailMapper.updateIsEstablishment(signProductCodeBO);
        saveLog(LogEventEnum.PRESERVATION,"签约计划","签约计划保存成功","versionId:"+versionId);
        return "保存成功";
    }

    /**
     * 验证签约与供货是否一致
     * @param signProductCodeBO
     * @param isParking
     * @return
     */
    public int checkSupplyAndSign(SignProductCodeBO signProductCodeBO, LocalDateTime affiliationMonth, Integer isParking) {

        BigDecimal one = BigDecimal.valueOf(1L);
        //供货详情
        BigDecimal supplyCount = BigDecimal.ZERO;
        BigDecimal supplyArea = BigDecimal.ZERO;
        BigDecimal supplyPrice = BigDecimal.ZERO;
        //签约详情
        BigDecimal signCount = BigDecimal.ZERO;
        BigDecimal signArea = BigDecimal.ZERO;
        BigDecimal signPrice = BigDecimal.ZERO;

        signProductCodeBO.setIsDel(IsDeleteEnum.NO.getKey());
        //查询供货详情
        SignProductDetailBO signProductDetailBO = signPlanVersionSupplyDetailMapper.selectCombinationCodeDetail(signProductCodeBO);
        supplyCount = BigDecimal.valueOf(signProductDetailBO.getRoomCount());
        supplyArea = signProductDetailBO.getRoomArea();
        supplyPrice = signProductDetailBO.getSumPrice();

        List<SignPlanVersionDetail> signPlanVersionDetailList = signPlanVersionDetailMapper.selectSignDetail(signProductCodeBO);

        for (SignPlanVersionDetail detail : signPlanVersionDetailList) {
            // 当前年月是否早于编制时间，若早于取签约计划详情数据，否则取签约实际详情数据
            if (LocalDateTimeUtils.compareDate(detail.getSignPlanTime(), affiliationMonth, 0)) {
                signCount = signCount.add(BigDecimal.valueOf(detail.getEarlySignRoomCountActual().longValue() + detail.getSupplySignRoomCountActual().longValue()));
                signArea = signArea.add(detail.getEarlySignRoomAreaActual()).add(detail.getSupplySignRoomAreaActual());
                signPrice = signPrice.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
            } else {
                signCount = signCount.add(detail.getEarlySignRoomCount().add(detail.getSupplySignRoomCount()));
                signArea = signArea.add(detail.getEarlySignRoomArea().add(detail.getSupplySignRoomArea()));
                signPrice = signPrice.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
            }
        }
        //套数、面积、金额的差异小于1,表示编制完成(车位不比较面积)
        if(WhetherEnum.YES.getKey() == isParking){
            if(supplyCount.subtract(signCount).abs().compareTo(one) < 0 && supplyPrice.subtract(signPrice).abs().compareTo(one) < 0 ) {
                return 1;
            }
        }else {
            if(supplyCount.subtract(signCount).abs().compareTo(one) < 0 && supplyArea.subtract(signArea).abs().compareTo(one) < 0 && supplyPrice.subtract(signPrice).abs().compareTo(one) < 0 ) {
                return 1;
            }
        }
        return 0;
    }

    /**
     * 对集合中最大签约时间一条签约数据，做补差处理
     * @param spddList
     * @param newSpddList
     * @param signPlanVersion
     */
    private int signPlanDetailCompensation(List<SignPlanDetailDto> spddList, SignPlanVersion signPlanVersion) {
        int isEstablishment = 0;

        spddList.sort(Comparator.comparing(SignPlanDetailDto::getSignPlanTime));
        SignPlanDetailDto signPlanDetailDto = spddList.get(spddList.size() - 1);
        //签约时间
        LocalDateTime signPlanTime = signPlanDetailDto.getSignPlanTime();
        //签约归属日期
        LocalDateTime time = LocalDateTimeUtils.toLocalDateTime(signPlanVersion.getAffiliationMonth());
        if (!signPlanTime.isBefore(time)) {
            BigDecimal one = new BigDecimal(1);
            BigDecimal earlySumPrice = new BigDecimal(signPlanDetailDto.getEarlySumPrice());
            BigDecimal earlySignSumPrice = new BigDecimal(signPlanDetailDto.getEarlySignSumPrice());
            BigDecimal earlyCanSignSumPrice = earlySumPrice.subtract(earlySignSumPrice);
            BigDecimal supplySumPrice = new BigDecimal(signPlanDetailDto.getSupplySumPrice());
            BigDecimal supplySignSumPrice = new BigDecimal(signPlanDetailDto.getSupplySignSumPrice());
            BigDecimal supplyCanSignSumPrice = supplySumPrice.subtract(supplySignSumPrice);
            //计算最后剩余的金额小于1元,则该业态表示已经编制完成
            if (supplyCanSignSumPrice.compareTo(one) < 0 && earlyCanSignSumPrice.compareTo(one) < 0) {
                signPlanDetailDto.setEarlySignSumPrice(signPlanDetailDto.getEarlySumPrice());
                signPlanDetailDto.setEarlySignAvgPrice(signPlanDetailDto.getEarlyAvgPrice());
                signPlanDetailDto.setEarlySignRoomArea(signPlanDetailDto.getEarlyRoomArea());
                signPlanDetailDto.setEarlySignRoomCount(signPlanDetailDto.getEarlyRoomCount());
                signPlanDetailDto.setEarlyCanSignRoomCount("0");
                signPlanDetailDto.setSupplySignSumPrice(signPlanDetailDto.getSupplySumPrice());
                signPlanDetailDto.setSupplySignAvgPrice(signPlanDetailDto.getSupplyAvgPrice());
                signPlanDetailDto.setSupplySignRoomArea(signPlanDetailDto.getSupplyRoomArea().toString());
                signPlanDetailDto.setSupplySignRoomCount(signPlanDetailDto.getSupplyRoomCount().toString());
                signPlanDetailDto.setSupplyCanSignRoomCount("0");
                isEstablishment =  1;
            } else {
                isEstablishment = 0;
            }
            spddList.remove(spddList.size() - 1);
            spddList.add(signPlanDetailDto);
        }

        return isEstablishment;
    }

    /**
     * 获取新增月份的签约计划
     * @param signPlanVersionDetail
     * @param newSpddList
     * @param affiliationMonth
     * @param alias
     * @return
     */
    private List<SignPlanVersionDetail> getNewSignPlanDetailList(SignPlanVersionDetail signPlanVersionDetail, List<SignPlanDetailDto> newSpddList, String affiliationMonth, String alias) {
        List<SignPlanVersionDetail> newSignPlanDetailList = new ArrayList<>();

        // 产品构成签约详情相同数据
        SignPlanVersionDetail spvd = new SignPlanVersionDetail();
        spvd.setVersionId(signPlanVersionDetail.getVersionId());
        spvd.setGroupId(signPlanVersionDetail.getGroupId());
        spvd.setGroupType(signPlanVersionDetail.getGroupType());
        spvd.setGroupCode(signPlanVersionDetail.getGroupCode());
        spvd.setGroupName(signPlanVersionDetail.getGroupName());
        spvd.setProductType(signPlanVersionDetail.getProductType());
        spvd.setProductCode(signPlanVersionDetail.getProductCode());
        spvd.setProductName(signPlanVersionDetail.getProductName());
        spvd.setProProductCode(signPlanVersionDetail.getProProductCode());
        spvd.setProProductType(signPlanVersionDetail.getProProductType());
        spvd.setBusinessType(signPlanVersionDetail.getBusinessType());
        spvd.setBusinessTypeName(signPlanVersionDetail.getBusinessTypeName());
        spvd.setFreeType(signPlanVersionDetail.getFreeType());
        spvd.setFreeTypeName(signPlanVersionDetail.getFreeTypeName());
        spvd.setProType(signPlanVersionDetail.getProType());
        spvd.setProTypeName(signPlanVersionDetail.getProTypeName());
        spvd.setIsParking(signPlanVersionDetail.getIsParking());
        spvd.setIsDelete(IsDeleteEnum.NO.getKey());
        spvd.setCreateBy(alias);
        spvd.setIsEstablishment(WhetherEnum.NO.getKey());

        for (SignPlanDetailDto spdd : newSpddList) {
            String yearMonth = LocalDateTimeUtils.formatTime(spdd.getSignPlanTime(), DateUtils.DATE_JFP_STR);
            if(LocalDateTimeUtils.compareYearMonth(yearMonth,affiliationMonth) < 0){
                continue;
            }
            SignPlanVersionDetail newspvd = new SignPlanVersionDetail();
            try {
                newspvd = spvd.clone();
            } catch (CloneNotSupportedException e) {
                log.error("###### errorMsg: ", e);
            }

            newspvd.setId(UUIDUtils.create());
            newspvd.setEarlyRoomCount(NumberUtils.accuracyBigDecimal(spdd.getEarlyRoomCount(), 5));
            newspvd.setEarlyCanSignRoomCount(NumberUtils.accuracyBigDecimal(spdd.getEarlyCanSignRoomCount(), 5));
            newspvd.setEarlyRoomArea(NumberUtils.accuracyBigDecimal(spdd.getEarlyRoomArea(), 4));
            newspvd.setEarlyAvgRoomArea(NumberUtils.accuracyBigDecimal(spdd.getEarlyAvgRoomArea(), 4));
            newspvd.setEarlyAvgPrice(NumberUtils.accuracyBigDecimal(spdd.getEarlyAvgPrice(), 4));
            newspvd.setEarlySumPrice(NumberUtils.accuracyBigDecimal(spdd.getEarlySumPrice(), 4));
            newspvd.setEarlySignRoomCount(NumberUtils.accuracyBigDecimal(spdd.getEarlySignRoomCount(), 5));
            newspvd.setEarlySignRoomArea(NumberUtils.accuracyBigDecimal(spdd.getEarlySignRoomArea(), 4));
            newspvd.setEarlySignAvgPrice(NumberUtils.accuracyBigDecimal(spdd.getEarlySignAvgPrice(), 4));
            newspvd.setEarlySignSumPrice(NumberUtils.accuracyBigDecimal(spdd.getEarlySignSumPrice(), 4));
            newspvd.setSupplyRoomCount(NumberUtils.accuracyBigDecimal(spdd.getSupplyRoomCount(), 5));
            newspvd.setSupplyCanSignRoomCount(NumberUtils.accuracyBigDecimal(spdd.getSupplyCanSignRoomCount(), 5));
            newspvd.setSupplyRoomArea(NumberUtils.accuracyBigDecimal(spdd.getSupplyRoomArea(), 4));
            newspvd.setSupplyAvgRoomArea(NumberUtils.accuracyBigDecimal(spdd.getSupplyAvgRoomArea(), 4));
            newspvd.setSupplyAvgPrice(NumberUtils.accuracyBigDecimal(spdd.getSupplyAvgPrice(), 4));
            newspvd.setSupplySumPrice(NumberUtils.accuracyBigDecimal(spdd.getSupplySumPrice(), 4));
            newspvd.setSupplySignRoomCount(NumberUtils.accuracyBigDecimal(spdd.getSupplySignRoomCount(), 5));
            newspvd.setSupplySignRoomArea(NumberUtils.accuracyBigDecimal(spdd.getSupplySignRoomArea(), 4));
            newspvd.setSupplySignAvgPrice(NumberUtils.accuracyBigDecimal(spdd.getSupplySignAvgPrice(), 4));
            newspvd.setSupplySignSumPrice(NumberUtils.accuracyBigDecimal(spdd.getSupplySignSumPrice(), 4));
            newspvd.setEarlySignRoomCountActual(NumberUtils.stringToInteger(spdd.getEarlySignRoomCountActual()));
            newspvd.setEarlySignRoomAreaActual(NumberUtils.accuracyBigDecimal(spdd.getEarlySignRoomAreaActual(), 4));
            newspvd.setEarlySignAvgPriceActual(NumberUtils.accuracyBigDecimal(spdd.getEarlySignAvgPriceActual(), 4));
            newspvd.setEarlySignSumPriceActual(NumberUtils.accuracyBigDecimal(spdd.getEarlySignSumPriceActual(), 4));
            newspvd.setSupplySignRoomCountActual(NumberUtils.stringToInteger(spdd.getSupplySignRoomCountActual()));
            newspvd.setSupplySignRoomAreaActual(NumberUtils.accuracyBigDecimal(spdd.getSupplySignRoomAreaActual(), 4));
            newspvd.setSupplySignAvgPriceActual(NumberUtils.accuracyBigDecimal(spdd.getSupplySignAvgPriceActual(), 4));
            newspvd.setSupplySignSumPriceActual(NumberUtils.accuracyBigDecimal(spdd.getSupplySignSumPriceActual(), 4));
            newspvd.setYearEalySignRoomCountActual(NumberUtils.stringToInteger(spdd.getYearEalySignRoomCountActual()));
            newspvd.setYearEalySignRoomAreaActual(NumberUtils.accuracyBigDecimal(spdd.getYearEalySignRoomAreaActual(), 4));
            newspvd.setYearEalySignAvgPriceActual(NumberUtils.accuracyBigDecimal(spdd.getYearEalySignAvgPriceActual(), 4));
            newspvd.setYearEalySignSumPriceActual(NumberUtils.accuracyBigDecimal(spdd.getYearEalySignSumPriceActual(), 4));
            newspvd.setIsLock(spdd.getIsLock());
            newspvd.setSignPlanTime(spdd.getSignPlanTime());
            newspvd.setCreateTime(LocalDateTime.now());
            newSignPlanDetailList.add(newspvd);
        }
        return newSignPlanDetailList;
    }

    /**
     * 修改null值为0
     * @param dto
     */
    private void nullToZero(SignPlanDetailDto dto){
        if(StringUtils.isEmpty(dto.getEarlyRoomCount())){
            dto.setEarlyRoomCount("0");
        }
        if(StringUtils.isEmpty(dto.getEarlyCanSignRoomCount())){
            dto.setEarlyCanSignRoomCount("0");
        }
        if(StringUtils.isEmpty(dto.getEarlyRoomArea())){
            dto.setEarlyRoomArea("0");
        }
        if(StringUtils.isEmpty(dto.getEarlyAvgRoomArea())){
            dto.setEarlyAvgRoomArea("0");
        }
        if(StringUtils.isEmpty(dto.getEarlyRoomCount())){
            dto.setEarlyRoomCount("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySumPrice())){
            dto.setEarlySumPrice("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignRoomCount())){
            dto.setEarlySignRoomCount("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignRoomArea())){
            dto.setEarlySignRoomArea("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignAvgPrice())){
            dto.setEarlySignAvgPrice("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignSumPrice())){
            dto.setEarlySignSumPrice("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignRoomCountActual())){
            dto.setEarlySignRoomCountActual("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignRoomAreaActual())){
            dto.setEarlySignRoomAreaActual("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignAvgPriceActual())){
            dto.setEarlySignAvgPriceActual("0");
        }
        if(StringUtils.isEmpty(dto.getEarlySignSumPriceActual())){
            dto.setEarlySignSumPriceActual("0");
        }
        if(StringUtils.isEmpty(dto.getSupplyRoomCount())){
            dto.setSupplyRoomCount("0");
        }
        if(StringUtils.isEmpty(dto.getSupplyCanSignRoomCount())){
            dto.setSupplyCanSignRoomCount("0");
        }
        if(StringUtils.isEmpty(dto.getSupplyRoomArea())){
            dto.setSupplyRoomArea("0");
        }
        if(StringUtils.isEmpty(dto.getSupplyAvgRoomArea())){
            dto.setSupplyAvgRoomArea("0");
        }
        if(StringUtils.isEmpty(dto.getSupplyAvgPrice())){
            dto.setSupplyAvgPrice("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySumPrice())){
            dto.setSupplySumPrice("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignRoomCount())){
            dto.setSupplySignRoomCount("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignRoomArea())){
            dto.setSupplySignRoomArea("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignAvgPrice())){
            dto.setSupplySignAvgPrice("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignSumPrice())){
            dto.setSupplySignSumPrice("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignRoomCountActual())){
            dto.setSupplySignRoomCountActual("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignRoomAreaActual())){
            dto.setSupplySignRoomAreaActual("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignAvgPriceActual())){
            dto.setSupplySignAvgPriceActual("0");
        }
        if(StringUtils.isEmpty(dto.getSupplySignSumPriceActual())){
            dto.setSupplySignSumPriceActual("0");
        }
    }

    /**
     * 查询签约版本全路径名称与来源版本全路径名称列表
     *
     * @param projectFid
     * @param versionType
     * @return
     */
    @Override
    public List<FullPathVo> getSignFullPathList(String projectFid, String versionType) {
        // 1、查询分期下该版本的签约计划版本列表
        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.selectByProjectFidAndVersionType(projectFid,
                versionType, IsDeleteEnum.NO.getKey());
        if (null == signPlanVersionList || signPlanVersionList.size() == 0) {
            return null;
        }
        // 2、生成版本全路径列表
        List<FullPathVo> fullPathVoList = new ArrayList<>();
        for (SignPlanVersion signPlanVersion : signPlanVersionList) {
            fullPathVoList.add(getSignVersionFullNameVo(signPlanVersion));
        }
        return fullPathVoList;
    }

    /**
     * 查询该分期最新版本
     *
     * @param projectFid 分期id
     * @return
     */
    @Override
    public String getNewVersionType(String projectFid) {
        String s = signPlanVersionMapper.selectNewVersionType(projectFid, IsDeleteEnum.NO.getKey());
        return s;
    }
    /**
     * 根据楼栋列表查询明源签约数据
     * @param mymSignList 明源签约记录列表
     * @param buildings   楼栋列表
     * @param dateTime    指定时间
     * @return
     */
    @Override
    public ContractDto getMymSignList(List<MymContractDto> mymSignList, List<String> buildings, LocalDateTime dateTime) {
        ContractDto dto = new ContractDto();

        //去重
        buildings = buildings.stream().distinct().collect(toList());

        // 套数
        dto.setNum(0);
        // 签约建筑面积
        BigDecimal bldArea = BigDecimal.ZERO;
        // 签约成交总价
        BigDecimal cjRmbTotal = BigDecimal.ZERO;
        // 补差金额
        BigDecimal bcTotal = BigDecimal.ZERO;
        // 补差面积
        BigDecimal bcArea = BigDecimal.ZERO;
        dto.setBldArea(bldArea);
        dto.setCjRmbTotal(cjRmbTotal);
        dto.setBcTotal(bcTotal);
        dto.setBcArea(bcArea);
        if (CollectionUtils.isEmpty(buildings)) {
            return dto;
        }
        if(CollectionUtils.isEmpty(mymSignList)){
            return dto;
        }
        //过滤'撤销签约'的签约数据
        mymSignList = mymSignList.stream().filter(s -> !"撤销签约".equals(s.getCloseReason())).collect(Collectors.toList());

        //明源签约记录分类
        List<MymContractDto> activationList = mymSignList.stream().filter(s -> ActivationEnum.ACTIVATION.getKey().equals(s.getStatus())).collect(Collectors.toList());
        List<MymContractDto> closeList = mymSignList.stream().filter(s -> ActivationEnum.CLOSE.getKey().equals(s.getStatus())).collect(Collectors.toList());
        List<MymContractDto> compensationList = activationList.stream().filter(s ->s.getAuditDate()!=null).collect(Collectors.toList());

        int YearMonth = LocalDateTimeUtils.toIntYearMonth(dateTime.getYear(), dateTime.getMonthValue());

        for (String building : buildings) {
            //签约
            for (MymContractDto m : mymSignList) {
                if (building.equals(m.getBldPrdID())) {
                    int ywgsDate = LocalDateTimeUtils.toIntYearMonth(m.getYwgsDate().getYear(), m.getYwgsDate().getMonthValue());
                    if (ywgsDate == YearMonth) {
                        dto.setNum(dto.getNum() + 1);
                        dto.setCjRmbTotal(StrUtils.BigDecimalADD(dto.getCjRmbTotal(), m.getCjRmbTotal()));
                        dto.setBldArea(StrUtils.BigDecimalADD(dto.getBldArea(), m.getBldArea()));
                    }
                }
            }
            //退房
            for (MymContractDto m : closeList) {
                if (building.equals(m.getBldPrdID())) {
                    int closeDate = LocalDateTimeUtils.toIntYearMonth(m.getCloseDate().getYear(), m.getCloseDate().getMonthValue());
                    if (closeDate == YearMonth) {
                        dto.setNum(dto.getNum() - 1);
                        dto.setCjRmbTotal(StrUtils.BigDecimalSub(dto.getCjRmbTotal(), m.getCjRmbTotal()));
                        dto.setBldArea(StrUtils.BigDecimalSub(dto.getBldArea(), m.getBldArea()));
                    }
                }
            }
            //补差
            for (MymContractDto m : compensationList) {
                if (building.equals(m.getBldPrdID())) {
                    int ywgsDate = LocalDateTimeUtils.toIntYearMonth(m.getAuditDate().getYear(), m.getAuditDate().getMonthValue());
                    if (ywgsDate == YearMonth) {
                        dto.setBcTotal(StrUtils.BigDecimalADD(dto.getBcTotal(), m.getBcTotal()));
                        dto.setBcArea(StrUtils.BigDecimalADD(dto.getBcArea(), m.getBcArea()));

                    }
                }
            }
        }

        return dto;
    }

    /**
     * 根据楼栋列表查询nos签约数据
     * @param nosSigningList nos签约记录列表
     * @param buildings      楼栋列表
     * @param dateTime       指定时间
     * @return
     */
    @Override
    public NosSignDto getNosSignList(List<NosTradedataDto> nosSigningList, List<String> buildings, LocalDateTime dateTime) {

        NosSignDto dto = new NosSignDto();
        Integer roomCount = 0; // 套数
        BigDecimal roomArea = BigDecimal.ZERO; // 成交面积
        BigDecimal tradeMoney = BigDecimal.ZERO; // 成交金额
        dto.setRoomCount(0);
        dto.setRoomArea(roomArea);
        dto.setTradeMoney(tradeMoney);
        //当楼栋列表为空时
        if (null == buildings || buildings.size() == 0) {
            return dto;
        }
        //去重
        buildings = buildings.stream().distinct().collect(toList());
        //签约记录分类
        List<NosTradedataDto> signingList = new ArrayList<>(); //nos签约记录列表
        List<NosTradedataDto> checkOutList = new ArrayList<>(); //nos退房记录列表
        List<NosTradedataDto> compensationList = new ArrayList<>(); //nos补差记录列表
        for (NosTradedataDto nos : nosSigningList) {
            String tradeStatus = nos.getTradeStatus();
            if (tradeStatus.equals(TradeStatusEnum.SIGNING.getKey())) {
                signingList.add(nos);
            } else if (tradeStatus.equals(TradeStatusEnum.CHECK_OUT.getKey())) {
                checkOutList.add(nos);
            } else if (tradeStatus.equals(TradeStatusEnum.COMPENSATION.getKey())) {
                compensationList.add(nos);
            }
            ;
        }

        int YearMonth = LocalDateTimeUtils.toIntYearMonth(dateTime.getYear(), dateTime.getMonthValue());

        for (String building : buildings) {
            //签约
            for (NosTradedataDto n : signingList) {
                if (building.equals(n.getBuildingId())) {
                    int tradeDate = LocalDateTimeUtils.toIntYearMonth(n.getTradeDate().getYear(), n.getTradeDate().getMonthValue());
                    if (tradeDate == YearMonth) {
                        dto.setRoomCount(dto.getRoomCount() + n.getRoomCount().intValue());
                        dto.setRoomArea(StrUtils.BigDecimalADD(dto.getRoomArea(), n.getRoomArea()));
                        dto.setTradeMoney(StrUtils.BigDecimalADD(dto.getTradeMoney(), n.getTradeMoney()));
                    }
                }
            }
            //退房
            for (NosTradedataDto n : checkOutList) {
                if (building.equals(n.getBuildingId())) {
                    int tradeDate = LocalDateTimeUtils.toIntYearMonth(n.getTradeDate().getYear(), n.getTradeDate().getMonthValue());
                    if (tradeDate == YearMonth) {
                        dto.setRoomCount(dto.getRoomCount() + n.getRoomCount().intValue());
                        dto.setRoomArea(StrUtils.BigDecimalADD(dto.getRoomArea(), n.getRoomArea()));
                        dto.setTradeMoney(StrUtils.BigDecimalADD(dto.getTradeMoney(), n.getTradeMoney()));
                    }
                }
            }
            //补差
            for (NosTradedataDto n : compensationList) {
                if (building.equals(n.getBuildingId())) {
                    int tradeDate = LocalDateTimeUtils.toIntYearMonth(n.getTradeDate().getYear(), n.getTradeDate().getMonthValue());
                    if (tradeDate == YearMonth) {
                        dto.setRoomCount(dto.getRoomCount() + n.getRoomCount().intValue());
                        dto.setRoomArea(StrUtils.BigDecimalADD(dto.getRoomArea(), n.getRoomArea()));
                        dto.setTradeMoney(StrUtils.BigDecimalADD(dto.getTradeMoney(), n.getTradeMoney()));
                    }
                }
            }
        }
        return dto;
    }

    /**
     * 查询门户代办
     * @param id 分期id+版本类型code
     * @return
     */
    @Override
    public PortalTodo getPortalTodo(String id) {
        //查询代办
        PortalTodo portalTodo = portalTodoMapper.selectByVersionIdAndTodoTypeCode(id,TodoTypeCodeEnum.SIGN_PLAN.getKey());
        if(null == portalTodo){
            return null;
        }
        String operatorType = portalTodo.getOperatorType();
        if(operatorType.equals("3") || operatorType.equals("4")){
            return null;
        }
        return portalTodo;
    }

    /**
     * 分期下全盘最新的供货版本是否存在可售数据
     * @param projectfId
     * @return
     */
    @Override
    public boolean getStageIsSaly(String projectfId) {
        //获取分期下全盘最新的货值版本
        PriceVersion priceVersion  = priceVersionService.getNewestPriceVersion(projectfId);
        if(null != priceVersion){
            //查询基于货值版本生成的最新供货版本
            SupplyPlanVersion supplyPlanVersion = supplyPlanVersionMapper.selectNewestVersionByPriceId(priceVersion.getId(), WhetherEnum.NO.getKey());
            if(null != supplyPlanVersion){
                //供货版本的版本阶段(战规前或战规后)
                String versionAttr = VersionAttrEnum.AFTER_WARPLAN.getKey();
                String versionType = supplyPlanVersion.getVersionType();
                if(VersionTypeEunm.BUDGET.getKey().equals(versionType)){
                    versionAttr = supplyPlanVersion.getVersionAttr();
                }else {
                    if(VersionTypeEunm.INVESTMENT.getKey().equals(versionType)){
                        versionAttr = VersionAttrEnum.BEFORE_WARPLAN.getKey();
                    }else {
                        versionAttr = VersionAttrEnum.AFTER_WARPLAN.getKey();
                    }
                }

                //战规前供货版本，查询产品构成层级是否存在可售数据
                if(VersionAttrEnum.BEFORE_WARPLAN.getKey().equals(versionAttr)){
                    Integer count = signPlanVersionMapper.selectSupplyIsSalableOrStageSelfSustainment(priceVersion.getId(),OrgTypeEunm.ORG_PRODUCT.getKey(),SalyTypeEnum.SALABLE.getKey(), SalyTypeEnum.STAGE_SELF_SUSTAINMENT.getKey());
                    if(count >0){
                        return true;
                    }
                }else {
                //战规后供货版本，查询楼栋层级是否存在可售数据
                    Integer count = signPlanVersionMapper.selectSupplyIsSalableOrStageSelfSustainment(priceVersion.getId(),OrgTypeEunm.ORG_BUILD.getKey(), SalyTypeEnum.SALABLE.getKey(), SalyTypeEnum.STAGE_SELF_SUSTAINMENT.getKey());
                    if(count >0){
                        return true;
                    }
                }
            }
        }

        return false;
    }



    /**
     * type :0  查询当月供货计划详细数据（总是取计划，对于当月之前已经实际供货的楼栋，不参与计算）
     * type :1  查询当年供货计划详细数据（总是取计划，对于当年之前已经实际供货的楼栋，不参与计算）
     * @param supplyDetailList 供货详情列表
     * @param dateTime 查询时间
     * @param isParking 是否车位楼栋
     * @param type 查询类型（0:月 ,1:年）
     * @return
     */
    @Override
    public SignProductDetailBO getSupplyPlanDetail(List<SignPlanVersionSupplyDetail> supplyDetailList, LocalDateTime dateTime, int isParking, int type) {
        SignProductDetailBO signProductDetailBO = new SignProductDetailBO();

        String time;
        if(type==0){
            time = LocalDateTimeUtils.formatTime(dateTime, "yyyy-MM");
        }else {
            time = LocalDateTimeUtils.formatTime(dateTime, "yyyy");
        }
        Integer roomCount = 0;
        BigDecimal roomArea = BigDecimal.ZERO;
        BigDecimal roomAvgArea = BigDecimal.ZERO;
        BigDecimal avgPrice = BigDecimal.ZERO;
        BigDecimal sumPrice = BigDecimal.ZERO;

        if(type==0){
            for (SignPlanVersionSupplyDetail spvd : supplyDetailList) {
                //计划供货时间
                LocalDateTime supplyPlanDate = spvd.getSupplyDate();
                String supplyPlanTime = LocalDateTimeUtils.formatTime(supplyPlanDate, "yyyy-MM");
                if (time.equals(supplyPlanTime)) {
                    if (spvd.getSupplyDateActual() != null || spvd.getSupplyTimeVirtual() != null) {
                        LocalDateTime supplyActualDate;
                        if(null != spvd.getSupplyTimeVirtual()){
                            supplyActualDate = spvd.getSupplyTimeVirtual();
                        }else {
                            supplyActualDate = spvd.getSupplyDateActual();
                        }
                        int yearMonthPlan = LocalDateTimeUtils.toIntYearMonth(supplyPlanDate.getYear(), supplyPlanDate.getMonthValue());
                        int yearMonthActual = LocalDateTimeUtils.toIntYearMonth(supplyActualDate.getYear(), supplyActualDate.getMonthValue());
                        //如当月之前已经实际供货，不参与计算
                        if (yearMonthActual < yearMonthPlan) {
                            continue;
                        }
                    }
                    if (isParking == WhetherEnum.YES.getKey()) {
                        roomCount = roomCount + spvd.getParkingNum();
                        sumPrice = sumPrice.add(spvd.getNotCommodityValue());

                    } else {
                        roomCount = roomCount + spvd.getRoomNum();
                        roomArea = roomArea.add(spvd.getSaleArea());
                        sumPrice = sumPrice.add(spvd.getHouseCommodityValue());
                    }
                }
            }
        }else if (type ==1){
            for (SignPlanVersionSupplyDetail spvd : supplyDetailList) {
                //计划供货时间
                LocalDateTime supplyPlanDate = spvd.getSupplyDate();
                String supplyPlanTime = LocalDateTimeUtils.formatTime(supplyPlanDate, "yyyy");
                if (time.equals(supplyPlanTime)) {
                    if (spvd.getSupplyDateActual() != null || spvd.getSupplyTimeVirtual() != null) {
                        LocalDateTime supplyActualDate;
                        if(null != spvd.getSupplyTimeVirtual()){
                            supplyActualDate = spvd.getSupplyTimeVirtual();
                        }else {
                            supplyActualDate = spvd.getSupplyDateActual();
                        }
                        int yearPlan = supplyPlanDate.getYear();
                        int yearActual = supplyActualDate.getYear();
                        //如当年之前已经实际供货，不参与计算
                        if (yearActual < yearPlan) {
                            continue;
                        }
                    }
                    if (isParking == WhetherEnum.YES.getKey()) {
                        roomCount = roomCount + spvd.getParkingNum();
                        sumPrice = sumPrice.add(spvd.getNotCommodityValue());

                    } else {
                        roomCount = roomCount + spvd.getRoomNum();
                        roomArea = roomArea.add(spvd.getSaleArea());
                        sumPrice = sumPrice.add(spvd.getHouseCommodityValue());

                    }
                }
            }
        }
        if (isParking == WhetherEnum.YES.getKey()) {
            avgPrice = BigDecimalUtils.divInteger(sumPrice, roomCount, 4);
        } else {
            roomAvgArea = BigDecimalUtils.divInteger(roomArea, roomCount, 4);
            avgPrice = BigDecimalUtils.divBigDecimal(sumPrice, roomArea, 4);
        }

        signProductDetailBO.setRoomCount(roomCount);
        signProductDetailBO.setRoomAvgArea(roomAvgArea);
        signProductDetailBO.setRoomArea(roomArea);
        signProductDetailBO.setAvgPrice(avgPrice);
        signProductDetailBO.setSumPrice(sumPrice);
        return signProductDetailBO;
    }


    /**
     * 查询当月供货实际详情数据
     *
     * @param supplyDetailList 签约计划供货列表
     * @param dateTime         指定时间
     * @param isParking        是否车位楼栋
     * @return
     */
    @Override
    public SignProductDetailBO getSupplyActualDetail(List<SignPlanVersionSupplyDetail> supplyDetailList, LocalDateTime dateTime, int isParking) {
        SignProductDetailBO signProductDetailBO = new SignProductDetailBO();

        String time = LocalDateTimeUtils.formatTime(dateTime, "yyyy-MM");
        Integer roomCount = 0;
        BigDecimal roomArea = BigDecimal.ZERO;
        BigDecimal roomAvgArea = BigDecimal.ZERO;
        BigDecimal avgPrice = BigDecimal.ZERO;
        BigDecimal sumPrice = BigDecimal.ZERO;

        for (SignPlanVersionSupplyDetail spvd : supplyDetailList) {

            LocalDateTime supplyActualDate;
            if(null != spvd.getSupplyTimeVirtual()){
                //虚拟拿证时间
                supplyActualDate = spvd.getSupplyTimeVirtual();
            }else {
                //实际供货时间
                supplyActualDate = spvd.getSupplyDateActual();
            }
            if (null == supplyActualDate ) {
                continue;
            }
            //实际供货年月
            String supplyActualTime = LocalDateTimeUtils.formatTime(supplyActualDate, "yyyy-MM");
            if (time.equals(supplyActualTime)) {
                if (isParking == WhetherEnum.YES.getKey()) {
                    roomCount = roomCount + spvd.getParkingNumActual();
                    sumPrice = sumPrice.add(spvd.getNotCommodityValueActual());

                } else {
                    roomCount = roomCount + spvd.getRoomNumActual();
                    roomArea = roomArea.add(spvd.getAreaActual());
                    sumPrice = sumPrice.add(spvd.getHouseCommodityValueActual());
                }
            }
        }
        if (isParking == WhetherEnum.YES.getKey()) {
            avgPrice = BigDecimalUtils.divInteger(sumPrice, roomCount, 4);
        } else {
            roomAvgArea = BigDecimalUtils.divInteger(roomArea, roomCount, 4);
            avgPrice = BigDecimalUtils.divBigDecimal(sumPrice, roomArea, 4);
        }
        signProductDetailBO.setRoomCount(roomCount);
        signProductDetailBO.setRoomArea(roomArea);
        signProductDetailBO.setRoomAvgArea(roomAvgArea);
        signProductDetailBO.setAvgPrice(avgPrice);
        signProductDetailBO.setSumPrice(sumPrice);
        return signProductDetailBO;
    }

    /**
     * 查询当月供货金额(有实际取月度实际供货金额，无实际取计划金额)
     *
     * @param supplyDetailList 供货详情列表
     * @param dateTime         指定时间
     * @param isParking        是否车位楼栋
     * @param type             查询类型（0 : 查询当年, 1 : 查询当月）
     * @return
     */
    @Override
    public SignProductDetailBO getSupplyDetail(List<SignSupplyDetailBO> supplyDetailList, LocalDateTime dateTime, int isParking, int type) {
        SignProductDetailBO signProductDetailBO = new SignProductDetailBO();
        Integer roomCount = 0;
        BigDecimal roomArea = BigDecimal.ZERO;
        BigDecimal avgPrice = BigDecimal.ZERO;
        BigDecimal sumPrice = BigDecimal.ZERO;
        if (type == 1) {
            //指定年月
            String time = LocalDateTimeUtils.formatTime(dateTime, "yyyy-MM");

            for (SignSupplyDetailBO signSupplyDetailBO : supplyDetailList) {
                //实际供货时间
                LocalDateTime supplyDate = signSupplyDetailBO.getSupplyDate();
                //实际供货年月
                String supplyTime = LocalDateTimeUtils.formatTime(supplyDate, "yyyy-MM");
                if (time.equals(supplyTime)) {
                    if (isParking == WhetherEnum.YES.getKey()) {
                        roomCount = roomCount + signSupplyDetailBO.getParkingNum();
                        sumPrice = sumPrice.add(signSupplyDetailBO.getNotCommodityValue());
                    } else {
                        roomCount = roomCount + signSupplyDetailBO.getRoomNum();
                        roomArea = roomArea.add(signSupplyDetailBO.getSaleArea());
                        sumPrice = sumPrice.add(signSupplyDetailBO.getHouseCommodityValue());
                    }
                }
            }
        } else if (type == 0) {
            String year = String.valueOf(dateTime.getYear());
            for (SignSupplyDetailBO signSupplyDetailBO : supplyDetailList) {
                //实际供货时间
                LocalDateTime supplyDate = signSupplyDetailBO.getSupplyDate();
                //实际供货年
                String supplyYear = LocalDateTimeUtils.formatTime(supplyDate, "yyyy");
                if (year.equals(supplyYear)) {
                    if (isParking == WhetherEnum.YES.getKey()) {
                        roomCount = roomCount + signSupplyDetailBO.getParkingNum();
                        sumPrice = sumPrice.add(signSupplyDetailBO.getNotCommodityValue());
                    } else {
                        roomCount = roomCount + signSupplyDetailBO.getRoomNum();
                        roomArea = roomArea.add(signSupplyDetailBO.getSaleArea());
                        sumPrice = sumPrice.add(signSupplyDetailBO.getHouseCommodityValue());
                    }
                }
            }

        }
        if (isParking == WhetherEnum.YES.getKey()) {
            avgPrice = BigDecimalUtils.divInteger(sumPrice, roomCount, 4);
        } else {
            avgPrice = BigDecimalUtils.divBigDecimal(sumPrice, roomArea, 4);
        }
        signProductDetailBO.setRoomCount(roomCount);
        signProductDetailBO.setRoomArea(roomArea);
        signProductDetailBO.setAvgPrice(avgPrice);
        signProductDetailBO.setSumPrice(sumPrice);
        return signProductDetailBO;
    }


    /**
     * 查询产品构成实际签约金额
     * @param spvdList  签约详情列表
     * @param isParking isParking
     * @return
     */
    private SignProductDetailBO getSignActualPrice(List<SignPlanVersionDetail> spvdList, Integer isParking) {
        SignProductDetailBO signProductDetailBO = new SignProductDetailBO();

        Integer roomCount = 0;
        BigDecimal roomArea = BigDecimal.ZERO;
        BigDecimal avgPrice = BigDecimal.ZERO;
        BigDecimal sumPrice = BigDecimal.ZERO;
        for (SignPlanVersionDetail spvd : spvdList) {
            roomCount = roomCount + spvd.getEarlySignRoomCountActual()+spvd.getSupplySignRoomCountActual();
            roomArea = roomArea.add(spvd.getEarlySignRoomAreaActual()).add(spvd.getSupplySignRoomAreaActual());
        	sumPrice = sumPrice.add(spvd.getEarlySignSumPriceActual()).add(spvd.getSupplySignSumPriceActual());

        }
        if (isParking == WhetherEnum.YES.getKey()) {
            avgPrice = BigDecimalUtils.divInteger(sumPrice, roomCount, 4);
        } else {
            avgPrice = BigDecimalUtils.divBigDecimal(sumPrice, roomArea, 4);
        }
        signProductDetailBO.setRoomCount(roomCount);
        signProductDetailBO.setRoomArea(roomArea);
        signProductDetailBO.setAvgPrice(avgPrice);
        signProductDetailBO.setSumPrice(sumPrice);
        return signProductDetailBO;
    }

    /**
     * 查询指定时间签约计划详细数据
     *
     * @param signPlanVersionDetailList 签约计划详情列表
     * @param dateTime                  指定时间
     * @return
     */
    @Override
    public SignPlanVersionDetail getSignPlanDetailByTime(List<SignPlanVersionDetail> signPlanVersionDetailList, LocalDateTime dateTime) {

        String signTime;
        String time = LocalDateTimeUtils.formatTime(dateTime, "yyyy-MM");
        for (SignPlanVersionDetail spvd : signPlanVersionDetailList) {
            signTime = LocalDateTimeUtils.formatTime(spvd.getSignPlanTime(), "yyyy-MM");
            if (time.equals(signTime)) {
                return spvd;
            }
        }

        return null;
    }

    /**
     * 查询指定时间年初存货实际签约金额
     *
     * @param signPlanVersionDetailList 签约计划详情列表
     * @param dateTime                  指定时间
     * @param isParking                是否车位楼栋
     * @return
     */
    @Override
    public SignProductDetailBO getYearEalySignSumPriceActualByTime(List<SignPlanVersionDetail> signPlanVersionDetailList, LocalDateTime dateTime, int isParking) {
        SignProductDetailBO signProductDetailBO = new SignProductDetailBO();

        Integer roomCount = 0;
        BigDecimal roomArea = BigDecimal.ZERO;
        BigDecimal avgPrice = BigDecimal.ZERO;
        BigDecimal sumPrice = BigDecimal.ZERO;
        String signTime;
        String time = LocalDateTimeUtils.formatTime(dateTime, "yyyy");
        for (SignPlanVersionDetail spvd : signPlanVersionDetailList) {
            signTime = LocalDateTimeUtils.formatTime(spvd.getSignPlanTime(), "yyyy");
            if (time.equals(signTime)) {
                roomCount += spvd.getYearEalySignRoomCountActual();
                roomArea=roomArea.add(spvd.getYearEalySignRoomAreaActual());
                sumPrice=sumPrice.add(spvd.getYearEalySignSumPriceActual());
            }
        }
        if(isParking==WhetherEnum.YES.getKey()){
            avgPrice= BigDecimalUtils.divInteger(sumPrice,roomCount,4);
        }else {
            avgPrice= BigDecimalUtils.divBigDecimal(sumPrice,roomArea,4);
        }
        signProductDetailBO.setRoomCount(roomCount);
        signProductDetailBO.setRoomArea(roomArea);
        signProductDetailBO.setAvgPrice(avgPrice);
        signProductDetailBO.setSumPrice(sumPrice);
        return signProductDetailBO;
    }


    /**
     * 产品构成签约数据向上归
     *
     * @param detailList 汇总集合
     * @param dtoList    被汇总的集合
     * @param orgType    被汇总的层级类型
     * @return
     */
    public List<SignPlanVersionDetailDto> signDataSorting(List<SignPlanVersionSupplyDetail> detailList,List<SignPlanVersionDetailDto> dtoList, String orgType) {

        List<SignPlanVersionDetailDto> signPlanVersionDetailDtoList = new ArrayList<>();

        for (SignPlanVersionSupplyDetail detail : detailList) {

            SignPlanVersionDetailDto spvdd = new SignPlanVersionDetailDto();
            spvdd.setSignDetailId(detail.getId());
            spvdd.setId(detail.getSupplyPlanId());
            spvdd.setPid(detail.getPid());
            spvdd.setType(detail.getType());
            spvdd.setName(detail.getOrgName());

            // 分期供货与签约
            SupplyAndSignDto supplyAndSign = new SupplyAndSignDto();

            // 可售货值套数
            String availableNum = NumberUtils.stringInitial(2,5);
            // 可售货值金额
            String availableAmount = NumberUtils.stringInitial(3,4);
            // 累计已供套数
            String accumulativeSupplyNum = NumberUtils.stringInitial(2,5);
            // 累计已供金额
            String accumulativeSupplyAmount = NumberUtils.stringInitial(3,4);
            // 累计已售套数
            String accumulativeSoldNum = NumberUtils.stringInitial(2,0);
            // 累计已售金额
            String accumulativeSold = NumberUtils.stringInitial(3,4);

            // 年初存货,年度新供,年度综合
            YearDepositNewSupplyDto yearDepositNewSupply = new YearDepositNewSupplyDto();

            // 年初存货套数
            String c_earlyInventoryNum = NumberUtils.stringInitial(2,5);
            // 年初存货金额
            String c_earlyInventoryAmount = NumberUtils.stringInitial(3,4);
            // 年初存货签约实际套数
            String c_actualContractNum = NumberUtils.stringInitial(2,5);
            // 年初存货签约实际金额
            String c_actualContractAmount = NumberUtils.stringInitial(3,4);
            // 年度新供供货计划套数
            String d_supplyPlanNum = NumberUtils.stringInitial(2,5);
            // 年度新供供货计划金额
            String d_supplyPlanAmount = NumberUtils.stringInitial(3,4);
            // 年度新供供货实际套数
            String d_supplyPlanActualNum = NumberUtils.stringInitial(2,5);
            // 年度新供供货实际金额
            String d_supplyPlanActualAmount = NumberUtils.stringInitial(3,4);
            // 年度新供签约实际套数
            String d_actualContractNum = NumberUtils.stringInitial(2,5);
            // 年度新供签约实际金额
            String d_actualContractAmount = NumberUtils.stringInitial(3,4);
            // 年度可售套数
            String e_annualSaleNum = NumberUtils.stringInitial(2,5);
            // 年度可售金额
            String e_annualSaleAmount = NumberUtils.stringInitial(3,4);
            // 年度签约目标套数
            String yearSigningTargetCount = NumberUtils.stringInitial(2,5);
            // 年度签约目标金额
            String yearSigningTargetAmount = NumberUtils.stringInitial(3,4);
            // 年度综合签约计划套数
            String e_signingPlanNum = NumberUtils.stringInitial(2,5);
            // 年度综合签约计划金额
            String e_signingPlanAmount = NumberUtils.stringInitial(3,4);

            // 被汇总的数据的层级是产品构成
            if (orgType.equals(OrgTypeEunm.ORG_PRODUCT.getKey())) {
                for (SignPlanVersionDetailDto spvd : dtoList) {
                    if (spvd.getPid().equals(detail.getSupplyPlanId())) {
                        Integer isParking = spvd.getIsParking();

                        SupplyAndSignDto sasd = spvd.getSupplyAndSign();
                        YearDepositNewSupplyDto ydnsd = spvd.getYearDepositNewSupply();

                        availableNum = NumberUtils.numAddcalculate(availableNum, sasd.getAvailableNum(), isParking);
                        availableAmount = NumberUtils.priceAddcalculate(availableAmount, sasd.getAvailableAmount(), isParking);
                        accumulativeSupplyNum = NumberUtils.numAddcalculate(accumulativeSupplyNum, sasd.getAccumulativeSupplyNum(), isParking);
                        accumulativeSupplyAmount = NumberUtils.priceAddcalculate(accumulativeSupplyAmount, sasd.getAccumulativeSupplyAmount(), isParking);
                        accumulativeSoldNum = NumberUtils.numAddcalculate(accumulativeSoldNum, sasd.getAccumulativeSoldNum(), isParking);
                        accumulativeSold = NumberUtils.priceAddcalculate(accumulativeSold, sasd.getAccumulativeSold(), isParking);

                        c_earlyInventoryNum = NumberUtils.numAddcalculate(c_earlyInventoryNum, ydnsd.getC_earlyInventoryNum(), isParking);
                        c_earlyInventoryAmount = NumberUtils.priceAddcalculate(c_earlyInventoryAmount, ydnsd.getC_earlyInventoryAmount(), isParking);
                        c_actualContractNum = NumberUtils.numAddcalculate(c_actualContractNum, ydnsd.getC_actualContractNum(), isParking);
                        c_actualContractAmount = NumberUtils.priceAddcalculate(c_actualContractAmount, ydnsd.getC_actualContractAmount(), isParking);
                        d_supplyPlanNum = NumberUtils.numAddcalculate(d_supplyPlanNum, ydnsd.getD_supplyPlanNum(), isParking);
                        d_supplyPlanAmount = NumberUtils.priceAddcalculate(d_supplyPlanAmount, ydnsd.getD_supplyPlanAmount(), isParking);
                        d_supplyPlanActualNum = NumberUtils.numAddcalculate(d_supplyPlanActualNum, ydnsd.getD_supplyPlanActualNum(), isParking);
                        d_supplyPlanActualAmount = NumberUtils.priceAddcalculate(d_supplyPlanActualAmount, ydnsd.getD_supplyPlanActualAmount(), isParking);
                        d_actualContractNum = NumberUtils.numAddcalculate(d_actualContractNum, ydnsd.getD_actualContractNum(), isParking);
                        d_actualContractAmount = NumberUtils.priceAddcalculate(d_actualContractAmount, ydnsd.getD_actualContractAmount(), isParking);
                        e_annualSaleNum = NumberUtils.numAddcalculate(e_annualSaleNum, ydnsd.getE_annualSaleNum(),isParking);
                        e_annualSaleAmount = NumberUtils.priceAddcalculate(e_annualSaleAmount, ydnsd.getE_annualSaleAmount(),isParking);
                        yearSigningTargetCount = NumberUtils.numAddcalculate(yearSigningTargetCount, ydnsd.getYearSigningTargetCount(),isParking);
                        yearSigningTargetAmount = NumberUtils.priceAddcalculate(yearSigningTargetAmount, ydnsd.getYearSigningTargetAmount(),isParking);
                        e_signingPlanNum = NumberUtils.numAddcalculate(e_signingPlanNum, ydnsd.getE_signingPlanNum(), isParking);
                        e_signingPlanAmount = NumberUtils.priceAddcalculate(e_signingPlanAmount, ydnsd.getE_signingPlanAmount(), isParking);

                    }
                }
            } else {
                for (SignPlanVersionDetailDto spvd : dtoList) {
                    if (spvd.getPid().equals(detail.getSupplyPlanId())) {

                        SupplyAndSignDto sasd = spvd.getSupplyAndSign();
                        YearDepositNewSupplyDto ydnsd = spvd.getYearDepositNewSupply();

                        availableNum = NumberUtils.numAdd(availableNum, sasd.getAvailableNum());
                        availableAmount = NumberUtils.priceAdd(availableAmount, sasd.getAvailableAmount());
                        accumulativeSupplyNum = NumberUtils.numAdd(accumulativeSupplyNum, sasd.getAccumulativeSupplyNum());
                        accumulativeSupplyAmount = NumberUtils.priceAdd(accumulativeSupplyAmount, sasd.getAccumulativeSupplyAmount());
                        accumulativeSoldNum = NumberUtils.numAdd(accumulativeSoldNum, sasd.getAccumulativeSoldNum());
                        accumulativeSold = NumberUtils.priceAdd(accumulativeSold, sasd.getAccumulativeSold());

                        c_earlyInventoryNum = NumberUtils.numAdd(c_earlyInventoryNum, ydnsd.getC_earlyInventoryNum());
                        c_earlyInventoryAmount = NumberUtils.priceAdd(c_earlyInventoryAmount, ydnsd.getC_earlyInventoryAmount());
                        c_actualContractNum = NumberUtils.numAdd(c_actualContractNum, ydnsd.getC_actualContractNum());
                        c_actualContractAmount = NumberUtils.priceAdd(c_actualContractAmount, ydnsd.getC_actualContractAmount());
                        d_supplyPlanNum = NumberUtils.numAdd(d_supplyPlanNum, ydnsd.getD_supplyPlanNum());
                        d_supplyPlanAmount = NumberUtils.priceAdd(d_supplyPlanAmount, ydnsd.getD_supplyPlanAmount());
                        d_supplyPlanActualNum = NumberUtils.numAdd(d_supplyPlanActualNum, ydnsd.getD_supplyPlanActualNum());
                        d_supplyPlanActualAmount = NumberUtils.priceAdd(d_supplyPlanActualAmount, ydnsd.getD_supplyPlanActualAmount());
                        d_actualContractNum = NumberUtils.numAdd(d_actualContractNum, ydnsd.getD_actualContractNum());
                        d_actualContractAmount = NumberUtils.priceAdd(d_actualContractAmount, ydnsd.getD_actualContractAmount());
                        e_annualSaleNum = NumberUtils.numAdd(e_annualSaleNum, ydnsd.getE_annualSaleNum());
                        e_annualSaleAmount = NumberUtils.priceAdd(e_annualSaleAmount, ydnsd.getE_annualSaleAmount());
                        yearSigningTargetCount = NumberUtils.numAdd(yearSigningTargetCount, ydnsd.getYearSigningTargetCount());
                        yearSigningTargetAmount = NumberUtils.priceAdd(yearSigningTargetAmount, ydnsd.getYearSigningTargetAmount());
                        e_signingPlanNum = NumberUtils.numAdd(e_signingPlanNum, ydnsd.getE_signingPlanNum());
                        e_signingPlanAmount = NumberUtils.priceAdd(e_signingPlanAmount, ydnsd.getE_signingPlanAmount());
                    }
                }
            }

            // 供货完成率（累计已供金额/可售货值金额）
            String deliveryCompletionRate = NumberUtils.pricePercentage(accumulativeSupplyAmount, availableAmount);
            // 签约完成率（累计已售金额/可售货值金额）
            String contractCompletionRate = NumberUtils.pricePercentage(accumulativeSold, availableAmount);
            // 实际去化率(累计已售金额/累计已供金额)
            String actualRemovalRate = NumberUtils.pricePercentage(accumulativeSold, accumulativeSupplyAmount);

            supplyAndSign.setAvailableNum(availableNum);
            supplyAndSign.setAvailableAmount(availableAmount);
            supplyAndSign.setAccumulativeSupplyNum(accumulativeSupplyNum);
            supplyAndSign.setAccumulativeSupplyAmount(accumulativeSupplyAmount);
            supplyAndSign.setAccumulativeSoldNum(accumulativeSoldNum);
            supplyAndSign.setAccumulativeSold(accumulativeSold);
            supplyAndSign.setDeliveryCompletionRate(deliveryCompletionRate);
            supplyAndSign.setContractCompletionRate(contractCompletionRate);
            supplyAndSign.setActualRemovalRate(actualRemovalRate);

            spvdd.setSupplyAndSign(supplyAndSign);

            // 年度综合签约实际套数(年初存货签约实际套数+年度新供签约实际套数)
            String e_signingActualNum = NumberUtils.numAdd(c_actualContractNum, d_actualContractNum);
            // 年度综合签约实际金额(年初存货签约实际金额+年度新供签约实际金额)
            String e_signingActualAmount = NumberUtils.priceAdd(c_actualContractAmount, d_actualContractAmount);
            // 年初存货实际去化率(年初存货签约实际金额/年初存货金额)
            String c_actualDeChemicalRateAmount = NumberUtils.pricePercentage(c_actualContractAmount, c_earlyInventoryAmount);
            // 年度新供供货达成率(年度新供供货实际金额/年度新供供货计划金额)
            String d_supplyPlanRateAmount = NumberUtils.pricePercentage(d_supplyPlanActualAmount, d_supplyPlanAmount);
            // 年度新供实际去化率(年度新供签约实际金额/年度新供供货实际金额)
            String d_actualDeChemicalRateAmount = NumberUtils.pricePercentage(d_actualContractAmount, d_supplyPlanActualAmount);
            // 年度综合签约达成率(年度综合签约实际金额/年度综合签约计划金额)
            String e_signingRateAmount = NumberUtils.pricePercentage(e_signingActualAmount, e_signingPlanAmount);
            // 年度实际去化率(年度综合签约实际金额/(年初存货金额+年度新供供货实际金额))
            String e_actualDeChemicalRateAmount = NumberUtils.pricePercentage(e_signingActualAmount, (NumberUtils.priceAdd(c_earlyInventoryAmount, d_supplyPlanActualAmount)));
            // 计划去化率（年度综合签约计划金额 / 年度可售金额）
            String e_planDeChemicalRateAmount = NumberUtils.pricePercentage(e_signingPlanAmount, e_annualSaleAmount);

            yearDepositNewSupply.setC_earlyInventoryNum(c_earlyInventoryNum);
            yearDepositNewSupply.setC_earlyInventoryAmount(c_earlyInventoryAmount);
            yearDepositNewSupply.setC_actualContractNum(c_actualContractNum);
            yearDepositNewSupply.setC_actualContractAmount(c_actualContractAmount);
            yearDepositNewSupply.setD_supplyPlanNum(d_supplyPlanNum);
            yearDepositNewSupply.setD_supplyPlanAmount(d_supplyPlanAmount);
            yearDepositNewSupply.setD_supplyPlanActualNum(d_supplyPlanActualNum);
            yearDepositNewSupply.setD_supplyPlanActualAmount(d_supplyPlanActualAmount);
            yearDepositNewSupply.setD_actualContractNum(d_actualContractNum);
            yearDepositNewSupply.setD_actualContractAmount(d_actualContractAmount);
            yearDepositNewSupply.setE_signingPlanNum(e_signingPlanNum);
            yearDepositNewSupply.setE_signingPlanAmount(e_signingPlanAmount);
            yearDepositNewSupply.setE_annualSaleNum(e_annualSaleNum);
            yearDepositNewSupply.setE_annualSaleAmount(e_annualSaleAmount);
            yearDepositNewSupply.setYearSigningTargetCount(yearSigningTargetCount);
            yearDepositNewSupply.setYearSigningTargetAmount(yearSigningTargetAmount);
            yearDepositNewSupply.setE_signingActualNum(e_signingActualNum);
            yearDepositNewSupply.setE_signingActualAmount(e_signingActualAmount);
            yearDepositNewSupply.setC_actualDeChemicalRateAmount(c_actualDeChemicalRateAmount);
            yearDepositNewSupply.setD_supplyPlanRateAmount(d_supplyPlanRateAmount);
            yearDepositNewSupply.setD_actualDeChemicalRateAmount(d_actualDeChemicalRateAmount);
            yearDepositNewSupply.setE_signingRateAmount(e_signingRateAmount);
            yearDepositNewSupply.setE_actualDeChemicalRateAmount(e_actualDeChemicalRateAmount);
            yearDepositNewSupply.setE_planDeChemicalRateAmount(e_planDeChemicalRateAmount);

            spvdd.setYearDepositNewSupply(yearDepositNewSupply);

            ////////////////////////////////////// 月份数据汇总///////////////////////////////////////////
            // 月初存货,月度新供,月度综合
            List<YearEarlyBottomDto> yearEarlyBottomList = new ArrayList<>();
            int monthName = 1;
            for (int i = 0; i < 12; i++) {
                YearEarlyBottomDto yearEarlyBottomDto = new YearEarlyBottomDto();
                yearEarlyBottomDto.setName(String.valueOf(monthName++));
                // 月初存货套数
                String f_monthlyInventoryNum = NumberUtils.stringInitial(2,5);
                // 月初存货金额
                String f_monthlyInventoryAmount = NumberUtils.stringInitial(3,4);
                // 月初存货签约计划套数
                String f_signingPlanNum = NumberUtils.stringInitial(2,5);
                // 月初存货签约计划金额
                String f_signingPlanAmount = NumberUtils.stringInitial(3,4);
                // 月初存货签约实际套数
                String f_signingActualNum = NumberUtils.stringInitial(2,5);
                // 月初存货签约实际金额
                String f_signingActualAmount = NumberUtils.stringInitial(3,4);
                // 月新供供货计划套数
                String g_supplyPlanNum = NumberUtils.stringInitial(2,5);
                // 月新供供货计划金额
                String g_supplyPlanAmount = NumberUtils.stringInitial(3,4);
                // 月新供供货实际套数
                String g_supplyPlanActualNum = NumberUtils.stringInitial(2,5);
                // 月新供供货实际金额
                String g_supplyPlanActualAmount = NumberUtils.stringInitial(3,4);
                // 月新供签约计划套数
                String g_signingPlanNum = NumberUtils.stringInitial(2,5);
                // 月新供签约计划金额
                String g_signingPlanAmount = NumberUtils.stringInitial(3,4);
                // 月新供签约实际套数
                String g_signingActualNum = NumberUtils.stringInitial(2,5);
                // 月新供签约实际金额
                String g_signingActualAmount = NumberUtils.stringInitial(3,4);
                // 月度综合月度可售套数
                String h_monthlyAvailableNum = NumberUtils.stringInitial(2,5);
                // 月度综合月度可售金额
                String h_monthlyAvailableAmount = NumberUtils.stringInitial(3,4);


                // 被汇总的数据的层级是产品构成
                if (orgType.equals(OrgTypeEunm.ORG_PRODUCT.getKey())) {
                    for (SignPlanVersionDetailDto spvd : dtoList) {
                        if (spvd.getPid().equals(detail.getSupplyPlanId())) {
                            Integer isParking = spvd.getIsParking();
                            List<YearEarlyBottomDto> spvdYearEarlyBottomList = spvd.getYearEarlyBottomList();

                            YearEarlyBottomDto iDto = spvdYearEarlyBottomList.get(i);

                            f_monthlyInventoryNum = NumberUtils.numAddcalculate(f_monthlyInventoryNum, iDto.getF_monthlyInventoryNum(), isParking);
                            f_monthlyInventoryAmount = NumberUtils.priceAddcalculate(f_monthlyInventoryAmount, iDto.getF_monthlyInventoryAmount(), isParking);
                            f_signingPlanNum = NumberUtils.numAddcalculate(f_signingPlanNum, iDto.getF_signingPlanNum(), isParking);
                            f_signingPlanAmount = NumberUtils.priceAddcalculate(f_signingPlanAmount, iDto.getF_signingPlanAmount(), isParking);
                            f_signingActualNum = NumberUtils.numAddcalculate(f_signingActualNum, iDto.getF_signingActualNum(), isParking);
                            f_signingActualAmount = NumberUtils.priceAddcalculate(f_signingActualAmount, iDto.getF_signingActualAmount(), isParking);
                            g_supplyPlanNum = NumberUtils.numAddcalculate(g_supplyPlanNum, iDto.getG_supplyPlanNum(), isParking);
                            g_supplyPlanAmount = NumberUtils.priceAddcalculate(g_supplyPlanAmount, iDto.getG_supplyPlanAmount(), isParking);
                            g_supplyPlanActualNum = NumberUtils.numAddcalculate(g_supplyPlanActualNum, iDto.getG_supplyPlanActualNum(), isParking);
                            g_supplyPlanActualAmount = NumberUtils.priceAddcalculate(g_supplyPlanActualAmount, iDto.getG_supplyPlanActualAmount(), isParking);
                            g_signingPlanNum = NumberUtils.numAddcalculate(g_signingPlanNum, iDto.getG_signingPlanNum(), isParking);
                            g_signingPlanAmount = NumberUtils.priceAddcalculate(g_signingPlanAmount, iDto.getG_signingPlanAmount(), isParking);
                            g_signingActualNum = NumberUtils.numAddcalculate(g_signingActualNum, iDto.getG_signingActualNum(), isParking);
                            g_signingActualAmount = NumberUtils.priceAddcalculate(g_signingActualAmount, iDto.getG_signingActualAmount(), isParking);
                            h_monthlyAvailableNum = NumberUtils.numAddcalculate(h_monthlyAvailableNum, iDto.getH_monthlyAvailableNum(), isParking);
                            h_monthlyAvailableAmount = NumberUtils.priceAddcalculate(h_monthlyAvailableAmount, iDto.getH_monthlyAvailableAmount(), isParking);
                        }
                    }
                } else {
                    for (SignPlanVersionDetailDto spvd : dtoList) {
                        if (spvd.getPid().equals(detail.getSupplyPlanId())) {
                            List<YearEarlyBottomDto> spvdYearEarlyBottomList = spvd.getYearEarlyBottomList();

                            YearEarlyBottomDto iDto = spvdYearEarlyBottomList.get(i);

                            f_monthlyInventoryNum = NumberUtils.numAdd(f_monthlyInventoryNum, iDto.getF_monthlyInventoryNum());
                            f_monthlyInventoryAmount = NumberUtils.priceAdd(f_monthlyInventoryAmount, iDto.getF_monthlyInventoryAmount());
                            f_signingPlanNum = NumberUtils.numAdd(f_signingPlanNum, iDto.getF_signingPlanNum());
                            f_signingPlanAmount = NumberUtils.priceAdd(f_signingPlanAmount, iDto.getF_signingPlanAmount());
                            f_signingActualNum = NumberUtils.numAdd(f_signingActualNum, iDto.getF_signingActualNum());
                            f_signingActualAmount = NumberUtils.priceAdd(f_signingActualAmount, iDto.getF_signingActualAmount());
                            g_supplyPlanNum = NumberUtils.numAdd(g_supplyPlanNum, iDto.getG_supplyPlanNum());
                            g_supplyPlanAmount = NumberUtils.priceAdd(g_supplyPlanAmount, iDto.getG_supplyPlanAmount());
                            g_supplyPlanActualNum = NumberUtils.numAdd(g_supplyPlanActualNum, iDto.getG_supplyPlanActualNum());
                            g_supplyPlanActualAmount = NumberUtils.priceAdd(g_supplyPlanActualAmount, iDto.getG_supplyPlanActualAmount());
                            g_signingPlanNum = NumberUtils.numAdd(g_signingPlanNum, iDto.getG_signingPlanNum());
                            g_signingPlanAmount = NumberUtils.priceAdd(g_signingPlanAmount, iDto.getG_signingPlanAmount());
                            g_signingActualNum = NumberUtils.numAdd(g_signingActualNum, iDto.getG_signingActualNum());
                            g_signingActualAmount = NumberUtils.priceAdd(g_signingActualAmount, iDto.getG_signingActualAmount());
                            h_monthlyAvailableNum = NumberUtils.numAdd(h_monthlyAvailableNum, iDto.getH_monthlyAvailableNum());
                            h_monthlyAvailableAmount = NumberUtils.priceAdd(h_monthlyAvailableAmount, iDto.getH_monthlyAvailableAmount());
                        }
                    }
                }
                // 月初存货签约达成率(月初存货签约实际金额/月初存货签约计划金额)
                String f_signingRateAmount = NumberUtils.pricePercentage(f_signingActualAmount, f_signingPlanAmount);
                // 月初存货计划去化率(月初存货签约计划金额/月初存货金额)
                String f_planDeChemicalRateAmount = NumberUtils.pricePercentage(f_signingPlanAmount, f_monthlyInventoryAmount);
                // 供货达成率(月度新供供货实际金额/月度新供供货计划金额)
                String g_supplyPlanRateAmount = NumberUtils.pricePercentage(g_supplyPlanActualAmount, g_supplyPlanAmount);
                // 月度新供签约达成率(月度新供签约实际金额/月度新供签约计划金额)
                String g_signingRateAmount = NumberUtils.pricePercentage(g_signingActualAmount, g_signingPlanAmount);
                // 月度新供实际去化率(月度新供签约计划金额/月度新供供货计划金额)
                String g_planDeChemicalRateAmount = NumberUtils.pricePercentage(g_signingPlanAmount, g_supplyPlanAmount);
                // 月度综合签约计划套数(月度新供签约计划套数+月初存货签约计划套数)
                String h_signingPlanNum = NumberUtils.numAdd(g_signingPlanNum, f_signingPlanNum);
                // 月度综合签约计划金额(月度新供签约计划金额+月初存货签约计划金额)
                String h_signingPlanAmount = NumberUtils.priceAdd(g_signingPlanAmount, f_signingPlanAmount);
                // 月度综合签约实际套数(月度新供签约实际套数+月初存货签约实际套数)
                String h_signingActualNum = NumberUtils.numAdd(g_signingActualNum, f_signingActualNum);
                // 月度综合签约实际金额(月度新供签约实际金额+月初存货签约实际金额)
                String h_signingActualAmount = NumberUtils.priceAdd(g_signingActualAmount, f_signingActualAmount);
                // 月度综合签约达成率(月度综合签约实际金额/月度综合签约计划金额)
                String h_signingRateAmount = NumberUtils.pricePercentage(h_signingActualAmount, h_signingPlanAmount);
                // 月度综合计划去化率(月度综合签约计划金额/月度综合月度可售金额)
                String h_planDeChemicalRateAmount = NumberUtils.pricePercentage(h_signingPlanAmount, h_monthlyAvailableAmount);

                yearEarlyBottomDto.setF_monthlyInventoryNum(f_monthlyInventoryNum);
                yearEarlyBottomDto.setF_monthlyInventoryAmount(f_monthlyInventoryAmount);
                yearEarlyBottomDto.setF_signingPlanNum(f_signingPlanNum);
                yearEarlyBottomDto.setF_signingPlanAmount(f_signingPlanAmount);
                yearEarlyBottomDto.setF_signingActualNum(f_signingActualNum);
                yearEarlyBottomDto.setF_signingActualAmount(f_signingActualAmount);
                yearEarlyBottomDto.setG_supplyPlanNum(g_supplyPlanNum);
                yearEarlyBottomDto.setG_supplyPlanAmount(g_supplyPlanAmount);
                yearEarlyBottomDto.setG_supplyPlanActualNum(g_supplyPlanActualNum);
                yearEarlyBottomDto.setG_supplyPlanActualAmount(g_supplyPlanActualAmount);
                yearEarlyBottomDto.setG_signingPlanNum(g_signingPlanNum);
                yearEarlyBottomDto.setG_signingPlanAmount(g_signingPlanAmount);
                yearEarlyBottomDto.setG_signingActualNum(g_signingActualNum);
                yearEarlyBottomDto.setG_signingActualAmount(g_signingActualAmount);
                yearEarlyBottomDto.setF_signingRateAmount(f_signingRateAmount);
                yearEarlyBottomDto.setF_planDeChemicalRateAmount(f_planDeChemicalRateAmount);
                yearEarlyBottomDto.setG_supplyPlanRateAmount(g_supplyPlanRateAmount);
                yearEarlyBottomDto.setG_signingRateAmount(g_signingRateAmount);
                yearEarlyBottomDto.setG_planDeChemicalRateAmount(g_planDeChemicalRateAmount);
                yearEarlyBottomDto.setH_monthlyAvailableNum(h_monthlyAvailableNum);
                yearEarlyBottomDto.setH_monthlyAvailableAmount(h_monthlyAvailableAmount);
                yearEarlyBottomDto.setH_signingPlanNum(h_signingPlanNum);
                yearEarlyBottomDto.setH_signingPlanAmount(h_signingPlanAmount);
                yearEarlyBottomDto.setH_signingActualNum(h_signingActualNum);
                yearEarlyBottomDto.setH_signingActualAmount(h_signingActualAmount);
                yearEarlyBottomDto.setH_signingRateAmount(h_signingRateAmount);
                yearEarlyBottomDto.setH_planDeChemicalRateAmount(h_planDeChemicalRateAmount);

                yearEarlyBottomList.add(yearEarlyBottomDto);
            }

            spvdd.setYearEarlyBottomList(yearEarlyBottomList);
            signPlanVersionDetailDtoList.add(spvdd);
        }

        return signPlanVersionDetailDtoList;
    }

    /**
     * 修改数值格式 金额单位万元
     * @param signPlanVersionDetailDtoList
     */
    @Override
    public List<SignPlanVersionDetailDto> unitConversion(List<SignPlanVersionDetailDto> signPlanVersionDetailDtoList) {
        if (signPlanVersionDetailDtoList.size() == 0) {
            return null;
        }
        for (SignPlanVersionDetailDto spvdd : signPlanVersionDetailDtoList) {
            // 数据层级类型为产品构成
            if (spvdd.getType().equals(OrgTypeEunm.ORG_PRODUCT.getKey())) {
                SupplyAndSignDto sasd = spvdd.getSupplyAndSign();
                sasd.setAvailableNum(NumberUtils.updateBigAccuracy(sasd.getAvailableNum(),2));
                sasd.setAvailableAmount(NumberUtils.fmtToWanUnit(sasd.getAvailableAmount()));
                sasd.setAccumulativeSupplyNum(NumberUtils.updateBigAccuracy(sasd.getAccumulativeSupplyNum(),2));
                sasd.setAccumulativeSupplyAmount(NumberUtils.fmtToWanUnit(sasd.getAccumulativeSupplyAmount()));
                sasd.setAccumulativeSoldNum(NumberUtils.updateBigAccuracy(sasd.getAccumulativeSoldNum(),2));
                sasd.setAccumulativeSold(NumberUtils.fmtToWanUnit(sasd.getAccumulativeSold()));

                YearDepositNewSupplyDto ydns = spvdd.getYearDepositNewSupply();
                ydns.setC_earlyInventoryNum(NumberUtils.updateBigAccuracy(ydns.getC_earlyInventoryNum(),2));
                ydns.setC_earlyInventoryAmount(NumberUtils.fmtToWanUnit(ydns.getC_earlyInventoryAmount()));
                ydns.setC_actualContractNum(NumberUtils.updateBigAccuracy(ydns.getC_actualContractNum(),2));
                ydns.setC_actualContractAmount(NumberUtils.fmtToWanUnit(ydns.getC_actualContractAmount()));
                ydns.setD_supplyPlanNum(NumberUtils.updateBigAccuracy(ydns.getD_supplyPlanNum(),2));
                ydns.setD_supplyPlanAmount(NumberUtils.fmtToWanUnit(ydns.getD_supplyPlanAmount()));
                ydns.setD_supplyPlanActualNum(NumberUtils.updateBigAccuracy(ydns.getD_supplyPlanActualNum(),2));
                ydns.setD_supplyPlanActualAmount(NumberUtils.fmtToWanUnit(ydns.getD_supplyPlanActualAmount()));
                ydns.setD_actualContractNum(NumberUtils.updateBigAccuracy(ydns.getD_actualContractNum(),2));
                ydns.setD_actualContractAmount(NumberUtils.fmtToWanUnit(ydns.getD_actualContractAmount()));
                ydns.setE_annualSaleNum(NumberUtils.updateBigAccuracy(ydns.getE_annualSaleNum(),2));
                ydns.setE_annualSaleAmount(NumberUtils.fmtToWanUnit(ydns.getE_annualSaleAmount()));
                ydns.setYearSigningTargetCount(NumberUtils.updateBigAccuracy(ydns.getYearSigningTargetCount(),2));
                ydns.setYearSigningTargetAmount(NumberUtils.fmtToWanUnit(ydns.getYearSigningTargetAmount()));
                ydns.setE_signingPlanNum(NumberUtils.updateBigAccuracy(ydns.getE_signingPlanNum(),2));
                ydns.setE_signingPlanAmount(NumberUtils.fmtToWanUnit(ydns.getE_signingPlanAmount()));
                ydns.setE_signingActualNum(NumberUtils.updateBigAccuracy(ydns.getE_signingActualNum(),2));
                ydns.setE_signingActualAmount(NumberUtils.fmtToWanUnit(ydns.getE_signingActualAmount()));

                List<YearEarlyBottomDto> yearEarlyBottomList = spvdd.getYearEarlyBottomList();
                for (YearEarlyBottomDto yebd : yearEarlyBottomList) {
                    yebd.setF_monthlyInventoryNum(NumberUtils.updateBigAccuracy(yebd.getF_monthlyInventoryNum(),2));
                    yebd.setF_monthlyInventoryAmount(NumberUtils.fmtToWanUnit(yebd.getF_monthlyInventoryAmount()));
                    yebd.setF_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getF_signingPlanNum(),2));
                    yebd.setF_signingPlanAmount(NumberUtils.fmtToWanUnit(yebd.getF_signingPlanAmount()));
                    yebd.setF_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getF_signingActualNum(),2));
                    yebd.setF_signingActualAmount(NumberUtils.fmtToWanUnit(yebd.getF_signingActualAmount()));
                    yebd.setG_supplyPlanNum(NumberUtils.updateBigAccuracy(yebd.getG_supplyPlanNum(),2));
                    yebd.setG_supplyPlanAmount(NumberUtils.fmtToWanUnit(yebd.getG_supplyPlanAmount()));
                    yebd.setG_supplyPlanActualNum(NumberUtils.updateBigAccuracy(yebd.getG_supplyPlanActualNum(),2));
                    yebd.setG_supplyPlanActualAmount(NumberUtils.fmtToWanUnit(yebd.getG_supplyPlanActualAmount()));
                    yebd.setG_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getG_signingPlanNum(),2));
                    yebd.setG_signingPlanAmount(NumberUtils.fmtToWanUnit(yebd.getG_signingPlanAmount()));
                    yebd.setG_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getG_signingActualNum(),2));
                    yebd.setG_signingActualAmount(NumberUtils.fmtToWanUnit(yebd.getG_signingActualAmount()));
                    yebd.setH_monthlyAvailableNum(NumberUtils.updateBigAccuracy(yebd.getH_monthlyAvailableNum(),2));
                    yebd.setH_monthlyAvailableAmount(NumberUtils.fmtToWanUnit(yebd.getH_monthlyAvailableAmount()));
                    yebd.setH_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getH_signingPlanNum(),2));
                    yebd.setH_signingPlanAmount(NumberUtils.fmtToWanUnit(yebd.getH_signingPlanAmount()));
                    yebd.setH_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getH_signingActualNum(),2));
                    yebd.setH_signingActualAmount(NumberUtils.fmtToWanUnit(yebd.getH_signingActualAmount()));
                }
            } else {
                SupplyAndSignDto sasd = spvdd.getSupplyAndSign();
                sasd.setAvailableNum(NumberUtils.updateBigAccuracy(sasd.getAvailableNum(),2));
                sasd.setAvailableAmount(NumberUtils.priceToWanAndMicrometer(sasd.getAvailableAmount()));
                sasd.setAccumulativeSupplyNum(NumberUtils.updateBigAccuracy(sasd.getAccumulativeSupplyNum(),2));
                sasd.setAccumulativeSupplyAmount(NumberUtils.priceToWanAndMicrometer(sasd.getAccumulativeSupplyAmount()));
                sasd.setAccumulativeSoldNum(NumberUtils.updateBigAccuracy(sasd.getAccumulativeSoldNum(),2));
                sasd.setAccumulativeSold(NumberUtils.priceToWanAndMicrometer(sasd.getAccumulativeSold()));

                YearDepositNewSupplyDto ydns = spvdd.getYearDepositNewSupply();
                ydns.setC_earlyInventoryNum(NumberUtils.updateBigAccuracy(ydns.getC_earlyInventoryNum(),2));
                ydns.setC_earlyInventoryAmount(NumberUtils.priceToWanAndMicrometer(ydns.getC_earlyInventoryAmount()));
                ydns.setC_actualContractNum(NumberUtils.updateBigAccuracy(ydns.getC_actualContractNum(),2));
                ydns.setC_actualContractAmount(NumberUtils.priceToWanAndMicrometer(ydns.getC_actualContractAmount()));
                ydns.setD_supplyPlanNum(NumberUtils.updateBigAccuracy(ydns.getD_supplyPlanNum(),2));
                ydns.setD_supplyPlanAmount(NumberUtils.priceToWanAndMicrometer(ydns.getD_supplyPlanAmount()));
                ydns.setD_supplyPlanActualNum(NumberUtils.updateBigAccuracy(ydns.getD_supplyPlanActualNum(),2));
                ydns.setD_supplyPlanActualAmount(NumberUtils.priceToWanAndMicrometer(ydns.getD_supplyPlanActualAmount()));
                ydns.setD_actualContractNum(NumberUtils.updateBigAccuracy(ydns.getD_actualContractNum(),2));
                ydns.setD_actualContractAmount(NumberUtils.priceToWanAndMicrometer(ydns.getD_actualContractAmount()));
                ydns.setE_annualSaleNum(NumberUtils.updateBigAccuracy(ydns.getE_annualSaleNum(),2));
                ydns.setE_annualSaleAmount(NumberUtils.priceToWanAndMicrometer(ydns.getE_annualSaleAmount()));
                ydns.setYearSigningTargetCount(NumberUtils.updateBigAccuracy(ydns.getYearSigningTargetCount(),2));
                ydns.setYearSigningTargetAmount(NumberUtils.priceToWanAndMicrometer(ydns.getYearSigningTargetAmount()));
                ydns.setE_signingPlanNum(NumberUtils.updateBigAccuracy(ydns.getE_signingPlanNum(),2));
                ydns.setE_signingPlanAmount(NumberUtils.priceToWanAndMicrometer(ydns.getE_signingPlanAmount()));
                ydns.setE_signingActualNum(NumberUtils.updateBigAccuracy(ydns.getE_signingActualNum(),2));
                ydns.setE_signingActualAmount(NumberUtils.priceToWanAndMicrometer(ydns.getE_signingActualAmount()));

                List<YearEarlyBottomDto> yearEarlyBottomList = spvdd.getYearEarlyBottomList();
                for (YearEarlyBottomDto yebd : yearEarlyBottomList) {
                    yebd.setF_monthlyInventoryNum(NumberUtils.updateBigAccuracy(yebd.getF_monthlyInventoryNum(),2));
                    yebd.setF_monthlyInventoryAmount(NumberUtils.priceToWanAndMicrometer(yebd.getF_monthlyInventoryAmount()));
                    yebd.setF_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getF_signingPlanNum(),2));
                    yebd.setF_signingPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getF_signingPlanAmount()));
                    yebd.setF_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getF_signingActualNum(),2));
                    yebd.setF_signingActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getF_signingActualAmount()));
                    yebd.setG_supplyPlanNum(NumberUtils.updateBigAccuracy(yebd.getG_supplyPlanNum(),2));
                    yebd.setG_supplyPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_supplyPlanAmount()));
                    yebd.setG_supplyPlanActualNum(NumberUtils.updateBigAccuracy(yebd.getG_supplyPlanActualNum(),2));
                    yebd.setG_supplyPlanActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_supplyPlanActualAmount()));
                    yebd.setG_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getG_signingPlanNum(),2));
                    yebd.setG_signingPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_signingPlanAmount()));
                    yebd.setG_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getG_signingActualNum(),2));
                    yebd.setG_signingActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_signingActualAmount()));
                    yebd.setH_monthlyAvailableNum(NumberUtils.updateBigAccuracy(yebd.getH_monthlyAvailableNum(),2));
                    yebd.setH_monthlyAvailableAmount(NumberUtils.priceToWanAndMicrometer(yebd.getH_monthlyAvailableAmount()));
                    yebd.setH_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getH_signingPlanNum(),2));
                    yebd.setH_signingPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getH_signingPlanAmount()));
                    yebd.setH_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getH_signingActualNum(),2));
                    yebd.setH_signingActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getH_signingActualAmount()));
                }
            }

        }
        return signPlanVersionDetailDtoList;
    }

    /**
     * 验证是否可以创建签约计划
     *
     * @param projectFid
     */
    public void isCreateSigPlan(String projectFid, String versionType) {
        // 根据分期id查询项目Id
        ProjectByStagesDto dto = projectByStagesMapper.selectByOrgdepid(projectFid);
        if (null == dto) {
            throw new MsgException(CodeEnum.STAGE_NON_EXISTENT);
        }
        if (!dto.getHrOrgTypeId().equals(HrOrgTypeEunm.ORG_32.getKey())) {
            throw new MsgException(CodeEnum.STAGE_NON_EXISTENT);
        }
        MdmStage mdmStage = stageService.selectById(projectFid);
        if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
            throw new MsgException(CodeEnum.CREATE_INACTIVE_STAGE_ERROR);
        }
        //获取分期下全盘最新的货值版本
        PriceVersion priceVersion  = priceVersionService.getNewestPriceVersion(projectFid);
        if(null == priceVersion){
            throw new MsgException(CodeEnum.NON_PRICE_VERSION_ERROR);
        }
        //查询基于货值版本生成的最新供货版本
        SupplyPlanVersion supplyPlanVersion = supplyPlanVersionMapper.selectNewestVersionByPriceId(priceVersion.getId(), WhetherEnum.NO.getKey());
        if(null == supplyPlanVersion){
            throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
        }
        if (!getStageIsSaly(projectFid)) {
            throw new MsgException(CodeEnum.CREATE_NOT_BUILD_SALY_TYPE_ERROR);
        }
        // 1、项目下存在待提交审批、审批中、审批被驳回的签约计划版本时，不允许新建签约计划版本
        List<String> versionStates = new ArrayList<>();
        versionStates.add(VersionStatusEnum.EXAMINEDAPPROVED.getKey());
        versionStates.add(VersionStatusEnum.APPROVED.getKey());
        versionStates.add(VersionStatusEnum.FAILUREAPPROVED.getKey());
        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper
                .selectByVersionStatesAndProjectId(dto.getParentOrgid(), versionStates, IsDeleteEnum.NO.getKey());
        if (signPlanVersionList.size() > 0) {
            throw new MsgException(CodeEnum.SIGNING_PROJECT_VERSIONSTATES_ERROR);
        }
        // 2、分期已存在待编制、编制中的签约计划版本时，不允许新建签约计划版本；此时可删除该分期待编制、编制中的签约计划版本后再新建
        versionStates = new ArrayList<>();
        versionStates.add(VersionStatusEnum.NOTPREPARED.getKey());
        versionStates.add(VersionStatusEnum.ORGANIZATION.getKey());
        List<SignPlanVersion> contractStagesList = signPlanVersionMapper.selectByVersionStatesAndProjectFId(projectFid,
                versionStates, IsDeleteEnum.NO.getKey());
        if (contractStagesList.size() > 0) {
            throw new MsgException(CodeEnum.SIGNING_PROJECTF_VERSIONSTATES_ERROR);
        }

        //查询项目下未审批通过的签约计划
        versionStates = new ArrayList<>();
        versionStates.add(VersionStatusEnum.NOTPREPARED.getKey());
        versionStates.add(VersionStatusEnum.ORGANIZATION.getKey());
        versionStates.add(VersionStatusEnum.EXAMINEDAPPROVED.getKey());
        versionStates.add(VersionStatusEnum.APPROVED.getKey());
        versionStates.add(VersionStatusEnum.FAILUREAPPROVED.getKey());
        List<SignPlanVersion> signPlanVersions = signPlanVersionMapper
                .selectByVersionStatesAndProjectId(dto.getParentOrgid(), versionStates, IsDeleteEnum.NO.getKey());
        //1.当前项目下任一分期存在未审批通过的【预算版】签约计划，所有分期均不可创建【非预算版】签约计划
        if (versionType.equals(VersionTypeEunm.INVESTMENT.getKey()) ||
                versionType.equals(VersionTypeEunm.WARPLAN.getKey()) ||
                versionType.equals(VersionTypeEunm.DYNAMIC.getKey())) {
            for (SignPlanVersion s : signPlanVersions) {
                if (s.getVersionType().equals(VersionTypeEunm.BUDGET.getKey())) {
                    throw new MsgException(CodeEnum.EXIST_UNAPPROVED_BUDGET_VERSIONSTATES_ERROR);
                }
            }

            //2.当前项目下任一分期存在未审批通过的【非预算版】签约计划，所有分期均不可创建【预算版】签约计划
        } else if (versionType.equals(VersionTypeEunm.BUDGET.getKey())) {
            for (SignPlanVersion s : signPlanVersions) {
                if (s.getVersionType().equals(VersionTypeEunm.INVESTMENT.getKey()) ||
                        s.getVersionType().equals(VersionTypeEunm.WARPLAN.getKey()) ||
                        s.getVersionType().equals(VersionTypeEunm.DYNAMIC.getKey())) {
                    throw new MsgException(CodeEnum.EXIST_UNAPPROVED_NOT_BUDGET_VERSIONSTATES_ERROR);
                }
            }
        }
    }


    /**
     * 查询签约计划审批预览
     */
    @Override
    public SigPlanPreviewVo getApprovalPreview(String projectId, String billId) {
        SigPlanPreviewVo vo = new SigPlanPreviewVo();
        ApprovalBill approvalBill = null;
        //提交审批时间
        String yearMonth = "";
        String versionState = "";
        if (StringUtils.isNotBlank(billId)) {
            //查询审批单内容缓存
            vo = (SigPlanPreviewVo)redisTemplateUtil.get(billId);
            if(null != vo){
                return vo;
            }
            vo = new SigPlanPreviewVo();
            // 根据审批单id查询当前项目id
            approvalBill = approvalBillMapper.selectById(billId);
            if (approvalBill != null) {
                if (StringUtils.isNotBlank(approvalBill.getProjectId())) {
                    projectId = approvalBill.getProjectId();
                    yearMonth = approvalBill.getPreviewDate();
                    versionState = approvalBill.getState();
                }
            }
        } else {
            String bill_Id = approvalBillMapper.selectByProjectIdAndTypeAndState(projectId, ProcessTypeEnum.SIGNING_PLAN.getKey(), versionState);
            if (StringUtils.isNotBlank(bill_Id)) {
                // 根据审批单id查询当前项目id
                approvalBill = approvalBillMapper.selectById(bill_Id);
                if (approvalBill != null) {
                    if (StringUtils.isNotBlank(approvalBill.getProjectId())) {
                        projectId = approvalBill.getProjectId();
                        yearMonth = approvalBill.getPreviewDate();
                        //流程审批通过和删除 都变为null
                        if (approvalBill.getState().equals(ApprovedStateCodeEnum.APPROVAL_SUCCESS.getKey())
                                || approvalBill.getState().equals(ApprovedStateCodeEnum.DELETE.getKey())
                                || approvalBill.getState().equals(ApprovedStateCodeEnum.ENDFLOW.getKey())) {
                            yearMonth = "";
                            approvalBill = null;
                        }
                    }
                }
            }
        }
        //查询项目编码和名称
        MdmProject mdm = projectByStagesMapper.selectByProjectId(projectId);
        if (null == mdm) {
            throw new MsgException(CodeEnum.SIGN_PLAN_VIEW_ERROR);
        }
        //区域名称
        String regionName = getRegionName(projectId);
        // 权益比例
        String equityRatio = projectBasicService.getEquityRatioRate(projectId);
        //获取本次编制的版本ids
        List<String> organizationIds = getOrganizationIds(projectId, approvalBill);
        //本次编制版本列表
        OrganizationDto organizationDto = getEditonNames(organizationIds, mdm);
        //全盘签约版本ids
        List<String> overallVersionIds = getOverallVersionIds(projectId, approvalBill);
        //若无提交审批,取当前时间
        if (StringUtils.isEmpty(yearMonth)) {
            yearMonth = LocalDateTimeUtils.formatTime(LocalDateTime.now(),"yyyy-MM");
        }
        //当前年
        String year = yearMonth.substring(0, 4);
        //获取当年及明年的全盘年度指标集合
        List<OverallYearIndex> overallYearIndexList = getOverallYearIndexList(projectId,yearMonth,overallVersionIds,approvalBill,organizationDto);
        // 月均销售同比偏差
        List<MonthSaleRateDeviationDto> msdList = getMonthAvgSaleRateDeviation(overallVersionIds,yearMonth);

        if(null != approvalBill){
            // 查询上传附件
            List<FilePreviewVO> files = fileUploadService.getFilePreview(approvalBill.getId());
            String instanceId = approvalBill.getInstanceId();
            String taskId = approvalBill.getTaskId();
            JSONObject instObject = new JSONObject();
            if(StringUtils.isNotEmpty(taskId)){
                instObject.put("proInstId",instanceId);
                instObject.put("taskId",taskId);
            }else {
                instObject.put("proInstId", instanceId);
            }
            String encode = null;
            try {
                encode = Base64.getEncoder().encodeToString(instObject.toString().getBytes("utf-8"));
            } catch (UnsupportedEncodingException e) {
                log.error("###### errorMsg: ", e);
            }
            String revoewUrl = reviewApprovalUrl + encode;

            vo.setInstanceId(instanceId);
            vo.setTaskId(taskId);
            vo.setState(approvalBill.getState());
            vo.setReviewUrl(revoewUrl);
            vo.setFiles(files);
            vo.setDescription(approvalBill.getDescription());
        }
        vo.setProjectId(projectId);
        vo.setProjectCode(mdm.getProjectCode());
        vo.setProjectName(mdm.getProjectName());
        vo.setRegionName(regionName);
        vo.setEquityRatio(equityRatio);
        vo.setOverallYearIndexList(overallYearIndexList);
        vo.setMonthSaleRateDeviationList(msdList);

        vo.setOverallVersionIds(overallVersionIds);

        if(CollectionUtils.isNotEmpty(overallYearIndexList)){
            // 全盘当年年度指标
            YearIndexDto yearIndex = overallYearIndexList.get(0).getYearIndex();
            if (CollectionUtils.isNotEmpty(yearIndex.getPkVersionIds())) {
                vo.setPkVersionIds(yearIndex.getPkVersionIds());
            } else {
                List<SignPlanEdition> pkVersionIds = new ArrayList<>();
                vo.setPkVersionIds(pkVersionIds);
            }
        }
        vo.setOrganizationDto(organizationDto);
        vo.setOrganizationIds(organizationIds);
        vo.setYearMonth(yearMonth);
        return vo;
    }

    /**
     * 判断签约版本中是否存在未编制完成的数据
     * @param versionIdList
     * @return
     */
    @Override
    public boolean isEstablishment(List<String> versionIdList) {
        //查询有没有编制完成
        int num = signPlanVersionDetailMapper.selectEstablishmentNum(versionIdList, IsDeleteEnum.NO.getKey(),
                IsDeleteEnum.NO.getKey());
        if (num >= 1) {
            return true;
        }
        return false;
    }

    /**
     * 获取当年及明年的全盘年度指标集合
     * @param projectId
     * @param yearMonth
     * @param versionIds
     * @param approvalBill
     * @param organizationDto
     * @return
     */
    private List<OverallYearIndex> getOverallYearIndexList(String projectId,String yearMonth, List<String> versionIds,ApprovalBill approvalBill, OrganizationDto organizationDto) {
        List<OverallYearIndex> overallYearIndexList = new ArrayList<>();

        int yearInt = Integer.valueOf(yearMonth.substring(0, 4));
        //根据签约计划版本id列表查询签约详细信息
        List<SignPlanVersionDetail> spvdList = signPlanVersionDetailMapper.selectByVersionIds(versionIds, IsDeleteEnum.NO.getKey());
        //取有签约计划金额的数据
        spvdList = spvdList.stream().filter(s -> s.getEarlySignSumPrice().compareTo(BigDecimal.ZERO) != 0
                || s.getEarlySignSumPriceActual().compareTo(BigDecimal.ZERO) != 0
                || s.getSupplySignSumPrice().compareTo(BigDecimal.ZERO) != 0
                || s.getSupplySignSumPriceActual().compareTo(BigDecimal.ZERO) != 0)
                .collect(Collectors.toList());
        //获取签约年份,去重
        Set<Integer> yearSet = spvdList.stream().map(s -> s.getSignPlanTime().getYear()).collect(Collectors.toSet());
        //升序排列,取大于等于当前年的年份集合
        List<Integer> yearList = yearSet.stream().sorted(Comparator.comparing(Integer::intValue))
                .filter(s -> s.intValue()>= yearInt)
                .collect(Collectors.toList());

        //遍历年份集合,计算前两年的全盘签约指标
        for (int i = 0; i < yearList.size(); i++) {
            Integer year = yearList.get(i);
            OverallYearIndex overallYearIndex = new OverallYearIndex();
            // 全盘当年年度指标
            YearIndexDto yearIndex = new YearIndexDto();
            if(year == yearInt){
                yearIndex = getOverallAnnualIndicators(projectId,versionIds,yearMonth, approvalBill, organizationDto);
            }else {
                yearMonth = LocalDateTimeUtils.getLocalFirstMonth(year.toString());
                yearIndex = getOverallAnnualIndicators(projectId,versionIds,yearMonth, approvalBill, organizationDto);
            }
            // 全盘当年月度指标
            List<YearEarlyBottomDto> yebList = getOverallMonthlyIndex(projectId, year, versionIds);
            overallYearIndex.setYear(year.toString());
            overallYearIndex.setYearIndex(yearIndex);
            overallYearIndex.setYearEarlyBottomList(yebList);
            overallYearIndexList.add(overallYearIndex);
            //计算前两年
            if(i==1){
                break;
            }
        }
        return overallYearIndexList;
    }

    /**
     * 查询全盘最新签约计划版本id列表映射
     * @return
     */
    @Override
    public Map<String,List<String>> getOverallSignVersionListMap() {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> afterWarplanTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //获取分期列表
        List<MdmStage> mdmStages = stageService.selectAll();
        Map<String, MdmStage> mdmStageMap = mdmStages.stream().collect(Collectors.toMap(s -> s.getProjectFid(), s -> s));

        //查询项目下的回款计划版本
        QueryWrapper<SignPlanVersion> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_delete",IsDeleteEnum.NO.getKey());
        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.selectList(queryWrapper);
        //按照分期分组(key:项目id,value:(key:分期id,value:签约计划列表))
        Map<String,Map<String, List<SignPlanVersion>>> versionListByProjectIdIdMap = signPlanVersionList.stream()
                .collect(Collectors.groupingBy(SignPlanVersion::getProjectId,Collectors.groupingBy(SignPlanVersion::getProjectfId)));

        Map<String,List<String>> versionIdListMap = new HashMap<>();
        for(Map.Entry<String, Map<String, List<SignPlanVersion>>> projectVersionEntry : versionListByProjectIdIdMap.entrySet()) {
            List<String> versionIdList = new ArrayList<>();
            for (Map.Entry<String, List<SignPlanVersion>> stageVersionEntry : projectVersionEntry.getValue().entrySet()) {
                MdmStage mdmStage = mdmStageMap.get(stageVersionEntry.getKey());
                if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
                    //剔除禁用分期
                    continue;
                }
                if(!getStageIsSaly(stageVersionEntry.getKey())){
                    //剔除全盘最新供货版本未全部可租或不可租售的分期
                    continue;
                }

                List<SignPlanVersion> versionList = stageVersionEntry.getValue();
                //查询分期下审批通过一房一价的最新版本
                SignPlanVersion version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                        .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);

                //是否一房一价
                if(null == version){
                    //获取获取战规后版本
                    version = versionList.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && afterWarplanTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                    if(null == version){
                        //战规前，取投资版、预算版中最新版签约计划
                        version = versionList.stream()
                                .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                                .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                    }else {
                        //战规后，取战规版、预算版、月度动态版中最新版
                        version = versionList.stream()
                                .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                                .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                    }
                }else {
                    //分期一房一价后，取预算版、月度动态版中最新版
                    version = versionList.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                }

                if(null != version){
                    versionIdList.add(version.getId());
                }
            }
            versionIdListMap.put(projectVersionEntry.getKey(),versionIdList);
        }
        return versionIdListMap;
    }

    /**
     * 查询全盘最新签约计划版本列表
     * @param projectId 项目id
     * @return
     */
    public List<String> getOverallSignVersionList(String projectId) {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> afterWarplanTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //查询项目下的签约计划版本
        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.selectByProjectId(projectId, IsDeleteEnum.NO.getKey());
        if(CollectionUtils.isEmpty(signPlanVersionList)){
            return null;
        }
        //按照分期分组(key:分期id,value:签约计划列表)
        Map<String, List<SignPlanVersion>> versionListByStageIdMap = signPlanVersionList.stream()
                .collect(Collectors.groupingBy(SignPlanVersion::getProjectfId));

        List<String> versionIdList = new ArrayList<>();
        for(Map.Entry<String, List<SignPlanVersion>> stageVersionEntry :versionListByStageIdMap.entrySet()){
            MdmStage mdmStage = stageService.selectById(stageVersionEntry.getKey());
            if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
                //剔除禁用分期
                continue;
            }
            if(!getStageIsSaly(stageVersionEntry.getKey())){
                //剔除全盘最新供货版本未全部可租或不可租售的分期
                continue;
            }

            List<SignPlanVersion> versionList = stageVersionEntry.getValue();
            //查询分期下审批通过一房一价的最新版本
            SignPlanVersion version = versionList.stream()
                    .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                    .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);

            //是否一房一价
            if(null == version){
                //获取获取战规后版本
                version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && afterWarplanTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                if(null == version){
                    //战规前，取投资版、预算版中最新版签约计划
                    version = versionList.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                }else {
                    //战规后，取战规版、预算版、月度动态版中最新版
                    version = versionList.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                }
            }else {
                //分期一房一价后，取预算版、月度动态版中最新版
                version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
            }

            if(null != version){
                versionIdList.add(version.getId());
            }
        }
        return versionIdList;
    }

    /**
     * 获取分期下全盘最新版本
     * @param projectfId
     * @return
     */
    @Override
    public SignPlanVersion getOverallByProjectf(String projectfId){
        MdmStage mdmStage = stageService.selectById(projectfId);
        if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
            //剔除禁用分期
            return null;
        }
        if(!getStageIsSaly(projectfId)){
            //剔除全盘最新供货版本未全部可租或不可租售的分期
            return null;
        }

        /**
         * 1、分期一房一价前
         *  (1)战规前：取投资版、预算版中最新版签约计划
         *  (1)战规后：取战规版、预算版、月度动态版中最新版签约计划
         * 2、分期一房一价后，取预算版、月度动态版中最新版签约计划
         */
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> afterWarplanTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());


        List<SignPlanVersion> versionList = signPlanVersionMapper.selectByProjectfId(projectfId,IsDeleteEnum.NO.getKey());
        if(CollectionUtils.isEmpty(versionList)){
            return null;
        }
        //查询分期下审批通过一房一价的最新版本
        SignPlanVersion version = versionList.stream()
                .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);

        //是否一房一价
        if(null == version){
            //获取获取战规后版本
            version = versionList.stream()
                    .filter(s-> successVersionStates.contains(s.getVersionState()) && afterWarplanTypes.contains(s.getVersionType()))
                    .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
            if(null == version){
                //战规前，取投资版、预算版中最新版签约计划
                version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
            }else {
                //战规后，取战规版、预算版、月度动态版中最新版
                version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
            }
        }else {
            //分期一房一价后，取预算版、月度动态版中最新版
            version = versionList.stream()
                    .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                    .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
        }

        return version;
    }

    /**
     * 查询指定时间全盘最新签约计划版本列表(供销综合分析表刷新使用)
     * @param projectId 项目id
     * @param yearMonth yyyy-MM
     * @return
     */
    public List<String> getOverallSignVersionList(String projectId,String yearMonth) {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> afterWarplanTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //查询项目下的签约计划版本
        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.selectByProjectIdLeYearMonth(projectId,yearMonth, IsDeleteEnum.NO.getKey());
        if(CollectionUtils.isEmpty(signPlanVersionList)){
            return null;
        }
        //按照分期分组(key:分期id,value:签约计划列表)
        Map<String, List<SignPlanVersion>> versionListByStageIdMap = signPlanVersionList.stream()
                .collect(Collectors.groupingBy(SignPlanVersion::getProjectfId));

        List<String> versionIdList = new ArrayList<>();
        for(Map.Entry<String, List<SignPlanVersion>> stageVersionEntry :versionListByStageIdMap.entrySet()){
            MdmStage mdmStage = stageService.selectById(stageVersionEntry.getKey());
            if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
                //剔除禁用分期
                continue;
            }
            if(!getStageIsSaly(stageVersionEntry.getKey())){
                //剔除全盘最新供货版本未全部可租或不可租售的分期
                continue;
            }

            List<SignPlanVersion> versionList = stageVersionEntry.getValue();
            //查询分期下审批通过一房一价的最新版本
            SignPlanVersion version = versionList.stream()
                    .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                    .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);

            //是否一房一价
            if(null == version){
                //获取获取战规后版本
                version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && afterWarplanTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                if(null == version){
                    //战规前，取投资版、预算版中最新版签约计划
                    version = versionList.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                }else {
                    //战规后，取战规版、预算版、月度动态版中最新版
                    version = versionList.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
                }
            }else {
                //分期一房一价后，取预算版、月度动态版中最新版
                version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(SignPlanVersion::getCreateTime)).orElse(null);
            }

            if(null != version){
                versionIdList.add(version.getId());
            }
        }
        return versionIdList;
    }


    /**
     * 计算产品构成数据
     *
     * @param productList      产品构成供货详情
     * @param sourceSupplyId   来源供货版本id
     * @param dateTime         查询时间
     * @param affiliationMonth 版本所属时间
     * @param groupId          组团id
     * @return
     */
    public List<SignPlanVersionDetailDto> calculateProductData(List<SignPlanVersionSupplyDetail> productList,String sourceSupplyId, LocalDateTime dateTime, LocalDateTime affiliationMonth, String groupId) {
        List<SignPlanVersionDetailDto> productDtoList = new ArrayList<>();

        for (SignPlanVersionSupplyDetail product : productList) {
            // 初始化签约供货详情查询条件
            SignProductCodeBO signSupplyBO = new SignProductCodeBO();
            signSupplyBO.setVersionId(product.getVersionId());
            signSupplyBO.setPid(product.getPid());
            signSupplyBO.setProductCode(product.getOrgCode());
            signSupplyBO.setBusinessType(product.getBusinessType());
            signSupplyBO.setFreeType(product.getFreeType());
            signSupplyBO.setProType(product.getProType());
            signSupplyBO.setIsDel(IsDeleteEnum.NO.getKey());
            // 初始化签约详情查询条件
            SignProductCodeBO signDetailBO = new SignProductCodeBO();
            signDetailBO.setVersionId(product.getVersionId());
            signDetailBO.setPid(groupId);
            signDetailBO.setProductCode(product.getOrgCode());
            signDetailBO.setBusinessType(product.getBusinessType());
            signDetailBO.setFreeType(product.getFreeType());
            signDetailBO.setProType(product.getProType());
            signDetailBO.setIsDel(IsDeleteEnum.NO.getKey());

            // 是否车位楼栋
            Integer isParking = product.getIsParking();

            SignPlanVersionDetailDto spvdd = new SignPlanVersionDetailDto();
            spvdd.setSignDetailId(product.getId());
            spvdd.setId(product.getSupplyPlanId());
            spvdd.setPid(product.getPid());
            spvdd.setType(product.getType());
            spvdd.setName(product.getOrgName());
            spvdd.setOrgCode(product.getOrgCode());
            spvdd.setBusinessType(product.getBusinessType());
            spvdd.setBusinessTypeName(product.getBusinessTypeName());
            spvdd.setFreeType(product.getFreeType());
            spvdd.setFreeTypeName(product.getFreeTypeName());
            spvdd.setProType(product.getProType());
            spvdd.setProTypeName(product.getProTypeName());
            spvdd.setProProductCode(product.getProProductCode());
            spvdd.setProProductName(product.getProProductType());
            spvdd.setIsParking(isParking);
            spvdd.setGroupId(groupId);

            LocalDateTime carryOverTime = signPlanVersionSupplyDetailMapper.selectNewestCarryOverTime(signSupplyBO);
            spvdd.setCarryOverTimeStr(null == carryOverTime ? null : LocalDateTimeUtils.formatTime(carryOverTime, "yyyy-MM-dd"));

            // 查询是否编制完成
            int count = signPlanVersionDetailMapper.selectIsEstablishment(signDetailBO);
            if (count > 0) {
                spvdd.setIsEstablishment(String.valueOf(WhetherEnum.NO.getKey()));
            } else {
                spvdd.setIsEstablishment(String.valueOf(WhetherEnum.YES.getKey()));
            }

            // 查询组团下产品构成类型供货详情列表
            List<SignPlanVersionSupplyDetail> supplyDetailList = signPlanVersionSupplyDetailMapper
                    .selectSupplyPlanDetail(signSupplyBO);
            if (CollectionUtils.isEmpty(supplyDetailList)) {
                continue;
            }
            // 取签约供货详情,有实际取实际，无实际取计划
            List<SignSupplyDetailBO> ssdBOList = switchSignSupplyDetailBO(supplyDetailList);
            //查询产品构成全部签约详情数据
            List<SignPlanVersionDetail> signPlanVersionDetailList = signPlanVersionDetailMapper.selectSignDetail(signDetailBO);

            ///////////////////////////////// 分期供货与签约///////////////////////////////
            SupplyAndSignDto supplyAndSignDto = new SupplyAndSignDto();

            /* 可售货值套数 */
            Integer availableNum = 0;
            /* 可售货值金额 */
            BigDecimal availableAmount = BigDecimal.ZERO;
            if (isParking.equals(WhetherEnum.YES.getKey())) {
                availableNum = product.getParkingNum();
                availableAmount = product.getNotCommodityValue();
            } else {
                availableNum = product.getRoomNum();
                availableAmount = product.getHouseCommodityValue();
            }

            /* 累计已供套数（累计到当前日已实际供货的套数） */
            Integer accumulativeSupplyNum = 0;
            /* 累计已供金额（累计到当前日已实际供货的金额） */
            BigDecimal accumulativeSupplyAmount = BigDecimal.ZERO;
            if (isParking.equals(WhetherEnum.YES.getKey())) {
                accumulativeSupplyNum = product.getParkingNumActual();
                accumulativeSupplyAmount = product.getNotCommodityValueActual();
            } else {
                accumulativeSupplyNum = product.getRoomNumActual();
                accumulativeSupplyAmount = product.getHouseCommodityValueActual();
            }
            //累计已售
            SignProductDetailBO signActualBo = getSignActualPrice(signPlanVersionDetailList, isParking);
            /* 累计已售套数(累计到当前日已实际签约的套数) */
            Integer accumulativeSoldNum = signActualBo.getRoomCount();
            /* 累计已售金额(累计到当前日已实际签约的金额) */
            BigDecimal accumulativeSold = signActualBo.getSumPrice();
            /* 供货完成率（累计已供金额/可售货值金额） */
            String deliveryCompletionRate = BigDecimalUtils.calculatedPercentage(accumulativeSupplyAmount, availableAmount);
            /* 签约完成率（累计已售金额/可售货值金额） */
            String contractCompletionRate = BigDecimalUtils.calculatedPercentage(accumulativeSold, availableAmount);
            /* 实际去化率(累计已售金额/累计已供金额) */
            String actualRemovalRate = BigDecimalUtils.calculatedPercentage(accumulativeSold, accumulativeSupplyAmount);

            supplyAndSignDto.setAccumulativeSoldNum(accumulativeSoldNum.toString());
            supplyAndSignDto.setAccumulativeSold(BigDecimalUtils.toString(accumulativeSold,4));
            supplyAndSignDto.setAccumulativeSupplyNum(accumulativeSupplyNum.toString());
            supplyAndSignDto.setAccumulativeSupplyAmount(BigDecimalUtils.toString(accumulativeSupplyAmount,4));
            supplyAndSignDto.setAvailableNum(availableNum.toString());
            supplyAndSignDto.setAvailableAmount(BigDecimalUtils.toString(availableAmount,4));
            supplyAndSignDto.setDeliveryCompletionRate(deliveryCompletionRate);
            supplyAndSignDto.setContractCompletionRate(contractCompletionRate);
            supplyAndSignDto.setActualRemovalRate(actualRemovalRate);
            spvdd.setSupplyAndSign(supplyAndSignDto);

            //////////////////////////////////// 月初存货,月度新供,月度综合/////////////////////////////////////////////
            List<YearEarlyBottomDto> yearEarlyBottomList = new ArrayList<>();
            LocalDateTime yearEarliestSupplyDate = LocalDateTimeUtils.getEarliestMonthDate(dateTime);
            LocalDateTime yearLatestSupplyDate = LocalDateTimeUtils.getLatestMonthDate(dateTime);
            int monthName = 1;
            for (LocalDateTime time = yearEarliestSupplyDate; LocalDateTimeUtils.compareDate(time, yearLatestSupplyDate,
                    1); time = LocalDateTimeUtils.addDate(time)) {
                YearEarlyBottomDto yearEarlyBottomDto = new YearEarlyBottomDto();
                yearEarlyBottomDto.setName(String.valueOf(monthName++));

                /////////////////////////////////////////////////////////////////////////////////////////////
                // 查询当月签约计划供货计划金额
                SignProductDetailBO monthSupplyPlanPrice = getSupplyPlanDetail(supplyDetailList, time, isParking,0);
                // 查询当月签约计划供货实际金额
                SignProductDetailBO monthSupplyActualPrice = getSupplyActualDetail(supplyDetailList, time, isParking);
                // 查询当月签约计划供货详情(有实际取月度实际供货金额，无实际取计划金额)
                SignProductDetailBO monthSupplyDetail = getSupplyDetail(ssdBOList, time, isParking, 1);
                // 查询当月签约计划详情
                SignPlanVersionDetail monthDetail = getSignPlanDetailByTime(signPlanVersionDetailList, time);

                ////////////////////////////////////////// 月初存货///////////////////////////////////////////////////
                /* 月初存货套数 */
                BigDecimal f_monthlyInventoryNum = null == monthDetail ? BigDecimal.ZERO : monthDetail.getEarlyRoomCount();
                /* 月初存货金额 */
                BigDecimal f_monthlyInventoryAmount = null == monthDetail ? BigDecimal.ZERO : monthDetail.getEarlySumPrice();
                /* 月初存货签约计划套数 */
                BigDecimal f_signingPlanNum = null == monthDetail ? BigDecimal.ZERO : monthDetail.getEarlySignRoomCount();
                /* 月初存货签约计划金额 */
                BigDecimal f_signingPlanAmount = null == monthDetail ? BigDecimal.ZERO : monthDetail.getEarlySignSumPrice();
                /* 月初存货签约实际套数 */
                Integer f_signingActualNum = null == monthDetail ? 0 : monthDetail.getEarlySignRoomCountActual();
                /* 月初存货签约实际金额 */
                BigDecimal f_signingActualAmount = null == monthDetail ? BigDecimal.ZERO : monthDetail.getEarlySignSumPriceActual();
                /* 月初存货签约达成率(月初存货签约实际金额/月初存货签约计划金额) */
                String f_signingRateAmount = BigDecimalUtils.calculatedPercentage(f_signingActualAmount, f_signingPlanAmount);
                /* 月初存货计划去化率(月初存货签约计划/月初存货) */
                String f_planDeChemicalRateAmount = BigDecimalUtils.calculatedPercentage(f_signingPlanAmount, f_monthlyInventoryAmount);

                /////////////////////////////////////////////月度新供///////////////////////////////////////////////
                /* 月度新供供货计划套数 */
                Integer g_supplyPlanNum = monthSupplyPlanPrice.getRoomCount();
                /* 月度新供供货计划金额 */
                BigDecimal g_supplyPlanAmount = monthSupplyPlanPrice.getSumPrice();
                /* 月度新供供货实际套数 */
                Integer g_supplyPlanActualNum = monthSupplyActualPrice.getRoomCount();
                /* 月度新供供货实际金额 */
                BigDecimal g_supplyPlanActualAmount = monthSupplyActualPrice.getSumPrice();
                /* 供货达成率(月度新供供货实际金额/月度新供供货计划金额) */
                String g_supplyPlanRateAmount = BigDecimalUtils.calculatedPercentage(g_supplyPlanActualAmount, g_supplyPlanAmount);
                /* 月度新供签约计划套数 */
                BigDecimal g_signingPlanNum = null == monthDetail ? BigDecimal.ZERO : monthDetail.getSupplySignRoomCount();
                /* 月度新供签约计划金额 */
                BigDecimal g_signingPlanAmount = null == monthDetail ? BigDecimal.ZERO : monthDetail.getSupplySignSumPrice();
                /* 月度新供签约实际套数 */
                Integer g_signingActualNum = null == monthDetail ? 0 : monthDetail.getSupplySignRoomCountActual();
                /* 月度新供签约实际金额 */
                BigDecimal g_signingActualAmount = null == monthDetail ? BigDecimal.ZERO : monthDetail.getSupplySignSumPriceActual();
                /* 月度新供签约达成率(月度新供签约实际金额/月度新供签约计划金额) */
                String g_signingRateAmount = BigDecimalUtils.calculatedPercentage(g_signingActualAmount, g_signingPlanAmount);
                /* 月度新供计划去化率(月度新供签约计划金额/月度新供供货计划金额) */
                String g_planDeChemicalRateAmount = BigDecimalUtils.calculatedPercentage(g_signingPlanAmount,g_supplyPlanAmount);

                /////////////////////////////////////////月度综合///////////////////////////////////////////
                /* 月度综合月度可售套数(月初存货套数+月度新供(有实际取月度实际供货，无实际取计划)) */
                BigDecimal h_monthlyAvailableNum = f_monthlyInventoryNum.add(new BigDecimal(monthSupplyDetail.getRoomCount()));
                /* 月度综合月度可售金额(月初存货金额+月度新供(有实际取月度实际供货，无实际取计划)) */
                BigDecimal h_monthlyAvailableAmount = f_monthlyInventoryAmount.add(monthSupplyDetail.getSumPrice());
                /* 月度综合签约计划套数(月度新供签约计划套数+月初存货签约计划套数) */
                BigDecimal h_signingPlanNum = g_signingPlanNum.add(f_signingPlanNum);
                /* 月度综合签约计划金额(月度新供签约计划金额+月初存货签约计划金额) */
                BigDecimal h_signingPlanAmount = g_signingPlanAmount.add(f_signingPlanAmount);
                /* 月度综合签约实际套数(月度新供签约套数+月初存货签约套数) */
                Integer h_signingActualNum = g_signingActualNum+ f_signingActualNum;
                /* 月度综合签约实际金额(月度新供签约实际+月初存货签约实际) */
                BigDecimal h_signingActualAmount = g_signingActualAmount.add(f_signingActualAmount);
                /* 月度综合签约达成率(月度综合签约实际金额/月度综合签约计划金额) */
                String h_signingRateAmount = BigDecimalUtils.calculatedPercentage(h_signingActualAmount,h_signingPlanAmount);
                // 月度综合计划去化率(月度综合签约计划/月度可售)
                String h_planDeChemicalRateAmount = BigDecimalUtils.calculatedPercentage(h_signingPlanAmount, h_monthlyAvailableAmount);


                yearEarlyBottomDto.setF_monthlyInventoryNum(BigDecimalUtils.toString(f_monthlyInventoryNum,4));
                yearEarlyBottomDto.setF_monthlyInventoryAmount(BigDecimalUtils.toString(f_monthlyInventoryAmount,4));
                yearEarlyBottomDto.setF_signingPlanNum(BigDecimalUtils.toString(f_signingPlanNum,4));
                yearEarlyBottomDto.setF_signingPlanAmount(BigDecimalUtils.toString(f_signingPlanAmount,4));
                yearEarlyBottomDto.setF_signingActualNum(f_signingActualNum.toString());
                yearEarlyBottomDto.setF_signingActualAmount(BigDecimalUtils.toString(f_signingActualAmount,4));
                yearEarlyBottomDto.setF_signingRateAmount(f_signingRateAmount);
                yearEarlyBottomDto.setF_planDeChemicalRateAmount(f_planDeChemicalRateAmount);
                yearEarlyBottomDto.setG_supplyPlanNum(g_supplyPlanNum.toString());
                yearEarlyBottomDto.setG_supplyPlanAmount(BigDecimalUtils.toString(g_supplyPlanAmount,4));
                yearEarlyBottomDto.setG_supplyPlanActualNum(g_supplyPlanActualNum.toString());
                yearEarlyBottomDto.setG_supplyPlanActualAmount(BigDecimalUtils.toString(g_supplyPlanActualAmount,4));
                yearEarlyBottomDto.setG_supplyPlanRateAmount(g_supplyPlanRateAmount);
                yearEarlyBottomDto.setG_signingPlanNum(BigDecimalUtils.toString(g_signingPlanNum,5));
                yearEarlyBottomDto.setG_signingPlanAmount(BigDecimalUtils.toString(g_signingPlanAmount,4));
                yearEarlyBottomDto.setG_signingActualNum(g_signingActualNum.toString());
                yearEarlyBottomDto.setG_signingActualAmount(BigDecimalUtils.toString(g_signingActualAmount,4));
                yearEarlyBottomDto.setG_signingRateAmount(g_signingRateAmount);
                yearEarlyBottomDto.setG_planDeChemicalRateAmount(g_planDeChemicalRateAmount);
                yearEarlyBottomDto.setH_monthlyAvailableNum(BigDecimalUtils.toString(h_monthlyAvailableNum,5));
                yearEarlyBottomDto.setH_monthlyAvailableAmount(BigDecimalUtils.toString(h_monthlyAvailableAmount,4));
                yearEarlyBottomDto.setH_signingPlanNum(BigDecimalUtils.toString(h_signingPlanNum,5));
                yearEarlyBottomDto.setH_signingPlanAmount(BigDecimalUtils.toString(h_signingPlanAmount,4));
                yearEarlyBottomDto.setH_signingActualNum(h_signingActualNum.toString());
                yearEarlyBottomDto.setH_signingActualAmount(BigDecimalUtils.toString(h_signingActualAmount,4));
                yearEarlyBottomDto.setH_signingRateAmount(h_signingRateAmount);
                yearEarlyBottomDto.setH_planDeChemicalRateAmount(h_planDeChemicalRateAmount);

                yearEarlyBottomList.add(yearEarlyBottomDto);
            }
            spvdd.setYearEarlyBottomList(yearEarlyBottomList);

            ///////////////////// 年初存货,年度新供,年度综合//////////////////////////////////
            YearDepositNewSupplyDto yearDepositNewSupplyDto = new YearDepositNewSupplyDto();
            String year = String.valueOf(dateTime.getYear());

            // 年初存货详情
            YearEarlyBottomDto yearEarlyBottomDto = yearEarlyBottomList.get(0);
            // 年初存货签约详情
            SignProductDetailBO yearEarlySignDetail = getYearEalySignSumPriceActualByTime(signPlanVersionDetailList, dateTime,isParking);
            // 当年供货计划详细数据(总是取计划，对于当月之前已经实际供货的楼栋，不参与计算)
            SignProductDetailBO yearSupplyPlan = getSupplyPlanDetail(supplyDetailList,dateTime, isParking,1);
            // 年度新供供货详情(有实际取月度实际供货金额，无实际取计划金额)
            SignProductDetailBO yearSupplyDetail = getSupplyDetail(ssdBOList, dateTime, isParking, 0);
            // 当年签约数据
            List<SignPlanVersionDetail> yearDetailList = signPlanVersionDetailList.stream().filter(s -> s.getSignPlanTime().toString().contains(year)).collect(Collectors.toList());

            // 年度签约目标套数
            BigDecimal yearSigningTargetCount = BigDecimal.ZERO;
            // 年度签约目标金额
            BigDecimal yearSigningTargetAmount = BigDecimal.ZERO;
            for (SignPlanVersionDetail detail : yearDetailList) {
                // 当前年月是否早于编制时间，若早于取签约计划详情数据，否则取签约实际详情数据
                if (LocalDateTimeUtils.compareDate(detail.getSignPlanTime(), affiliationMonth, 0)) {
                    yearSigningTargetCount = yearSigningTargetCount.add(BigDecimal.valueOf(detail.getEarlySignRoomCountActual().longValue() + detail.getSupplySignRoomCountActual().longValue()));
                    yearSigningTargetAmount = yearSigningTargetAmount.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
                } else {
                    yearSigningTargetCount = yearSigningTargetCount.add(detail.getEarlySignRoomCount().add(detail.getSupplySignRoomCount()));
                    yearSigningTargetAmount = yearSigningTargetAmount.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                }
            }

            // 年度新供供货实际套数
            Integer d_supplyPlanActualNum = 0;
            // 年度新供供货实际金额
            BigDecimal d_supplyPlanActualAmount = BigDecimal.ZERO;
            // 年度签约计划套数
            BigDecimal e_signingPlanNum = BigDecimal.ZERO;
            // 年度签约计划金额
            BigDecimal e_signingPlanAmount = BigDecimal.ZERO;
            // 年度综合签约实际套数
            Integer e_signingActualNum = 0;
            // 年度综合签约实际金额
            BigDecimal e_signingActualAmount = BigDecimal.ZERO;
            for (YearEarlyBottomDto earlyBottomDto : yearEarlyBottomList) {
                d_supplyPlanActualNum = d_supplyPlanActualNum + Integer.valueOf(earlyBottomDto.getG_supplyPlanActualNum());
                d_supplyPlanActualAmount = d_supplyPlanActualAmount.add(new BigDecimal(earlyBottomDto.getG_supplyPlanActualAmount()));
                e_signingPlanNum = e_signingPlanNum.add(new BigDecimal(earlyBottomDto.getH_signingPlanNum()));
                e_signingPlanAmount = e_signingPlanAmount.add(new BigDecimal(earlyBottomDto.getH_signingPlanAmount()));
                e_signingActualNum = e_signingActualNum + Integer.valueOf(earlyBottomDto.getH_signingActualNum());
                e_signingActualAmount = e_signingActualAmount.add(new BigDecimal(earlyBottomDto.getH_signingActualAmount()));
            }

            /////////////////////////// 年初存货////////////////////////////////////////////////
            // 年初实际存货套数=1月月初存货套数
            BigDecimal c_earlyInventoryNum = new BigDecimal(yearEarlyBottomDto.getF_monthlyInventoryNum());
            // 年初实际存货金额=1月月初存货金额
            BigDecimal c_earlyInventoryAmount = new BigDecimal(yearEarlyBottomDto.getF_monthlyInventoryAmount());
            // 年初存货实际签约套数
            Integer c_actualContractNum = yearEarlySignDetail.getRoomCount();
            // 年初存货实际签约金额
            BigDecimal c_actualContractAmount = yearEarlySignDetail.getSumPrice();
            // <年初存货>实际去化率金额(年初存货签约实际/年初存货)
            String c_actualDeChemicalRateAmount = BigDecimalUtils.calculatedPercentage(c_actualContractAmount, c_earlyInventoryAmount);

            /////////////////////////////////////// 年度新供/////////////////////////////////////////
            // 年度新供供货计划套数
            Integer d_supplyPlanNum = yearSupplyPlan.getRoomCount();
            // 年度新供供货计划金额
            BigDecimal d_supplyPlanAmount = yearSupplyPlan.getSumPrice();
            /* 年度新供供货达成率(年度新供供货实际金额/年度新供供货计划金额) */
            String d_supplyPlanRateAmount = BigDecimalUtils.calculatedPercentage(d_supplyPlanActualAmount, d_supplyPlanAmount);
            // 年度新供签约实际套数 = 年度综合签约实际套数 - 年初存货签约实际套数
            Integer d_actualContractNum = e_signingActualNum - c_actualContractNum;
            // 年度新供签约实际金额 = 年度综合签约实际金额 - 年初存货签约实际金额
            BigDecimal d_actualContractAmount = e_signingActualAmount.subtract(c_actualContractAmount);
            /* 年度新供实际去化率(年度新供签约实际/年度新供供货实际) */
            String d_actualDeChemicalRateAmount = BigDecimalUtils.calculatedPercentage(d_actualContractAmount,d_supplyPlanActualAmount);
            ////////////////////////////// xxxx年度综合///////////////////////////////////////////
            // 年度可售套数(年初存货+年度新供(有实际取月度实际供货，无实际取计划))
            BigDecimal e_annualSaleNum = c_earlyInventoryNum.add(new BigDecimal(yearSupplyDetail.getRoomCount()));
            // 年度可售金额(年初存货+年度新供(有实际取月度实际供货，无实际取计划))
            BigDecimal e_annualSaleAmount = c_earlyInventoryAmount.add(yearSupplyDetail.getSumPrice());
            // 年度综合签约达成率(年度签约实际金额/年度签约计划金额)
            String e_signingRateAmount = BigDecimalUtils.calculatedPercentage(e_signingActualAmount,e_signingPlanAmount);
            // 年度实际去化率(年度签约实际金额/（年初存货+年度新供供货实际）))
            String e_actualDeChemicalRateAmount = BigDecimalUtils.calculatedPercentage(e_signingActualAmount, c_earlyInventoryAmount.add(d_supplyPlanActualAmount));
            // 计划去化率（年度签约计划金额 / 年度可售金额）
            String e_planDeChemicalRateAmount = BigDecimalUtils.calculatedPercentage(e_signingPlanAmount, e_annualSaleAmount);

            yearDepositNewSupplyDto.setC_earlyInventoryNum(BigDecimalUtils.toString(c_earlyInventoryNum,5));
            yearDepositNewSupplyDto.setC_earlyInventoryAmount(BigDecimalUtils.toString(c_earlyInventoryAmount,4));
            yearDepositNewSupplyDto.setC_actualContractNum(c_actualContractNum.toString());
            yearDepositNewSupplyDto.setC_actualContractAmount(BigDecimalUtils.toString(c_actualContractAmount,4));
            yearDepositNewSupplyDto.setC_actualDeChemicalRateAmount(c_actualDeChemicalRateAmount);
            yearDepositNewSupplyDto.setD_supplyPlanNum(d_supplyPlanNum.toString());
            yearDepositNewSupplyDto.setD_supplyPlanAmount(BigDecimalUtils.toString(d_supplyPlanAmount,4));
            yearDepositNewSupplyDto.setD_supplyPlanActualNum(d_supplyPlanActualNum.toString());
            yearDepositNewSupplyDto.setD_supplyPlanActualAmount(BigDecimalUtils.toString(d_supplyPlanActualAmount,4));
            yearDepositNewSupplyDto.setD_supplyPlanRateAmount(d_supplyPlanRateAmount);
            yearDepositNewSupplyDto.setD_actualContractNum(d_actualContractNum.toString());
            yearDepositNewSupplyDto.setD_actualContractAmount(BigDecimalUtils.toString(d_actualContractAmount,4));
            yearDepositNewSupplyDto.setD_actualDeChemicalRateAmount(d_actualDeChemicalRateAmount);
            yearDepositNewSupplyDto.setE_annualSaleNum(BigDecimalUtils.toString(e_annualSaleNum,5));
            yearDepositNewSupplyDto.setE_annualSaleAmount(BigDecimalUtils.toString(e_annualSaleAmount,4));
            yearDepositNewSupplyDto.setYearSigningTargetCount(BigDecimalUtils.toString(yearSigningTargetCount,5));
            yearDepositNewSupplyDto.setYearSigningTargetAmount(BigDecimalUtils.toString(yearSigningTargetAmount,4));
            yearDepositNewSupplyDto.setE_signingPlanNum(BigDecimalUtils.toString(e_signingPlanNum,5));
            yearDepositNewSupplyDto.setE_signingPlanAmount(BigDecimalUtils.toString(e_signingPlanAmount,4));
            yearDepositNewSupplyDto.setE_signingActualNum(e_signingActualNum.toString());
            yearDepositNewSupplyDto.setE_signingActualAmount(BigDecimalUtils.toString(e_signingActualAmount,4));
            yearDepositNewSupplyDto.setE_signingRateAmount(e_signingRateAmount);
            yearDepositNewSupplyDto.setE_actualDeChemicalRateAmount(e_actualDeChemicalRateAmount);
            yearDepositNewSupplyDto.setE_planDeChemicalRateAmount(e_planDeChemicalRateAmount);

            spvdd.setYearDepositNewSupply(yearDepositNewSupplyDto);
            productDtoList.add(spvdd);
        }
        return productDtoList;
    }

    /**
     * 本次编制版本 分期: xxx版 版本号(组合起来) 预算版提交审批只有预算版所有分期 其他版本是所有版本分期
     *
     * @return
     */
    public List<String> getCompilingVersion(String projectId, String versionType) {
        List<String> stageNames = new ArrayList<>();

        List<String> str = Arrays.asList(VersionStatusEnum.ORGANIZATION.getKey(),VersionStatusEnum.EXAMINEDAPPROVED.getKey(),VersionStatusEnum.FAILUREAPPROVED.getKey());

        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.selectByProjectIdAndVersionState(projectId,
                str, versionType, IsDeleteEnum.NO.getKey());
        for (SignPlanVersion sp : signPlanVersionList) {
            String stageName = sp.getStageName();
            String versionNum = sp.getVersionNum();
            String versionName = StrUtils.getVersionName(sp.getVersionType());
            String combination = stageName + ": " + versionName + " " + versionNum;
            stageNames.add(combination);
        }
        return stageNames;
    }

    /**
     * 获取区域名称
     *
     * @param projectDto
     * @return
     */
    public String getRegionName(String projectId) {
        // 根据项目id获取组织机构全路径
        ProjectByStagesDto projectByStagesDto = projectByStagesMapper.selectByOrgdepid(projectId);
        List<String> orgids = new ArrayList<>();
        // 解析组织机构全路径
        String[] orgnofullpath = projectByStagesDto.getOrgnofullpath().split("-");
        for (String str : orgnofullpath) {
            orgids.add(str);
        }
        if(CollectionUtils.isEmpty(orgids)){
        	return "";
        }
        // 跟全路径id获取区域名称
        String regionName = projectByStagesMapper.selectRegionName(orgids, HrOrgTypeEunm.ORG_20.getKey());
        return regionName;
    }

    /**
     * 全盘2019年度指标
     */
    public YearIndexDto getOverallAnnualIndicators(String projectId, List<String> versionIds,String yearMonth,ApprovalBill approvalBill, OrganizationDto organizationDto) {
        YearIndexDto dto = new YearIndexDto();

        int flag = organizationDto.getFlag();
        String versionTypeCode = organizationDto.getVersionTypeCode();
        String year = yearMonth.substring(0, 4);
        //年初（当年1月）
        String earlyYearMonth = LocalDateTimeUtils.getLocalFirstMonth(year);
        //当前时间-01
        String openTimeActual = yearMonth + "-01";

        // 年初存货金额
        BigDecimal c_earlyInventoryAmount = signPlanVersionDetailMapper.selectEarlyInventoryAmount(versionIds, IsDeleteEnum.NO.getKey(), earlyYearMonth);
        //年度新供金额
        BigDecimal yearNewSupplyAmount = getYearNewSupplyAmount(versionIds, year,OrgTypeEunm.ORG_PRODUCT.getKey());
        // 年度总可售金额 = 年初存货金额+年度新供金额
        BigDecimal yearTotalSalableAmount = StrUtils.BigDecimalADD(c_earlyInventoryAmount, yearNewSupplyAmount);

        // 调整后年度签约目标金额
        BigDecimal yearSigningTargetAmount = getYearSignTarget(versionIds,yearMonth,1);
        // 年度综合去化率  = 年度签约目标金额/年度总可售
        String yearComprehensiveRate = StrUtils.rateAchievement(yearSigningTargetAmount, yearTotalSalableAmount);
        // 年度结转去化率 需C1提供
        String yearCarryForwardRate = "0";
        // 年初全盘未供
        BigDecimal earlyYearWholesaleSupply = getEarlyYearWholesaleSupplyAmount(versionIds, year);
        // 总未售 = 年初存货 + 年初全盘未供
        BigDecimal unsold = StrUtils.BigDecimalADD(c_earlyInventoryAmount, earlyYearWholesaleSupply);
        // 周转系数 = 年度签约目标金额 / 总未售
        BigDecimal turnoverCoefficient = StrUtils.divideV2(yearSigningTargetAmount, unsold);
        //版本对比差异集合
        List<DifferencesDto> differencesDtoList = getPkInfo(projectId,flag,yearSigningTargetAmount,year,approvalBill);
        //pk去重集合
        List<SignPlanEdition> pkVersionIds = new ArrayList<>();
        //pkid往上面放
        for (DifferencesDto ddo : differencesDtoList) {
            if (CollectionUtils.isNotEmpty(ddo.getPkVersionIds())) {
                pkVersionIds.addAll(ddo.getPkVersionIds());
            }
        }
        BigDecimal AnnualTarget = BigDecimal.ZERO;
        //签约差异值
        String contractDifference= "0";
        //若提交版本中有动态版，对比全盘年度指标
        if(versionTypeCode.equals(VersionTypeEunm.DYNAMIC.getKey())){
            //筛选调整后年度签约目标金额差异
            List<DifferencesDto> vsBeforeAdjustmentList = differencesDtoList.stream().filter(s -> s.getName().equals(VersionContrastType.VSBEFOREADJUSTMMENT.getValue())).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(vsBeforeAdjustmentList)){
                //年度指标变动差异 = 调整后年度签约目标金额差异
                BigDecimal amout = vsBeforeAdjustmentList.stream().map(s-> new BigDecimal(s.getAmount())).reduce(BigDecimal.ZERO,BigDecimal::add);
                BigDecimal wan = BigDecimal.valueOf(10000);
                //差值大于1万元,全盘年度指标视为有差异
                if(amout.compareTo(wan) > 0){
                    contractDifference = "1";
                }
            }
        }

        dto.setYearEarlyInventoryAmount(c_earlyInventoryAmount.toString());
        dto.setYearNewSupplyAmount(yearNewSupplyAmount.toString());
        dto.setYearTotalSalableAmount(yearTotalSalableAmount.toString());
        dto.setYearSigningTargetAmount(yearSigningTargetAmount.toString());
        dto.setYearComprehensiveRate(yearComprehensiveRate.replace("%", ""));
        dto.setYearCarryForwardRate(yearCarryForwardRate);
        dto.setTurnoverCoefficient(turnoverCoefficient.toString());
        dto.setDifferencesDto(differencesDtoList);
        dto.setPkVersionIds(pkVersionIds);
        dto.setContractDifference(contractDifference);
        return dto;
    }

    /**
     * 获取全盘年度签约目标
     * 1、非预算版(战规、动态: 已过月份取实际,未过月份取计划;若是投资版: 全取计划)
     * 2、预算版（查看来源版本类型,参考1）
     * @param versionIds 版本id集合
     * @param yearMonth 审批发起时间
     * @param type 0:调整前;1:调整后
     * @return
     */
    @Override
    public BigDecimal getYearSignTarget(List<String> versionIds, String yearMonth, int type) {
        BigDecimal sumAmount = BigDecimal.ZERO;

        String year = yearMonth.substring(0, 4);
        String beginTime = year + "-01";
        String endTime = year + "-12";

        for (String id : versionIds){
            //查询版本
            SignPlanVersion signPlanVersion = signPlanVersionMapper.selectById(id);
            //版本属性
            String versionAttr = signPlanVersion.getVersionAttr();

            //1.若是投资版，全取计划
            if(VersionAttrEnum.BEFORE_WARPLAN.getKey().equals(versionAttr)){
                List ids = new ArrayList<>();
                ids.add(id);
                BigDecimal amount = signPlanVersionDetailMapper.selectYearSigningTargetAmount(ids, year,IsDeleteEnum.NO.getKey());
                sumAmount = sumAmount.add(amount);
            }else {
                //2.战规版或动态版已过月份取实际,未过月份取计划
                String affiliationMonth = signPlanVersion.getAffiliationMonth();

                //2.1 若版本归属时间等于版本提交审批时间,说明在签约数据在本地表中有存储,需要查询本地表数据
                if(yearMonth.equals(affiliationMonth)){
                    //查询版本下指定时间段的签约实际金额
                    BigDecimal signActualPrice = signPlanVersionDetailMapper.selectSignActualPriceByTimeHorizon(id,beginTime,yearMonth,IsDeleteEnum.NO.getKey());
                    //查询版本下指定时间段的签约计划金额
                    BigDecimal signPlanPrice = signPlanVersionDetailMapper.selectSignPlanPriceByTimeHorizon(id,yearMonth,endTime,IsDeleteEnum.NO.getKey());
                    sumAmount = sumAmount.add(signActualPrice).add(signPlanPrice);
                }else {
                    //2.2 判断是否调整后
                    if(WhetherEnum.YES.getKey() == type ){
                        //2.2.1 调整后,若不相等,视为跨月审批,以版本提交审批时间为准,已过月份取实际,未过月份取计划,需要查询明源或Nos视图表数据
                        String projectfId = signPlanVersion.getProjectfId();
                        //查询签约视图取数系统
                        SysProjectFetchNumber sysProjectFetchNumber = sysProjectFetchNumberService.selectByFid(projectfId);
                        String countingSystemId = sysProjectFetchNumber.getCountingSystemId();
                        //查询版本下全部楼栋id
                        List<String> buildingList = signPlanVersionSupplyDetailMapper.selectBuildingIdList(id,IsDeleteEnum.NO.getKey());

                        //计算签约实际数据
                        List<NosTradedataDto> nosSigningList = new ArrayList<>();
                        List<MymContractDto> mymSignList = new ArrayList<>();
                        BigDecimal signActualPrice = BigDecimal.ZERO;
                        if(CollectionUtils.isNotEmpty(buildingList)){
                            if (countingSystemId.equals(FetchNnumberEnum.NOS.getKey())) {
                                //查询指定楼栋中全部nos签约数据
                                nosSigningList = dmOdsNosTradedataviewformarsService.selectNosContractList(buildingList);
                                signActualPrice = getNosSignPrice(nosSigningList,beginTime,yearMonth);
                            } else if (countingSystemId.equals(FetchNnumberEnum.MY.getKey())) {
                                //查询指定楼栋中全部明源签约数据
                                mymSignList = dmOdsMyVsMarsSContractService.selectMymContractList(buildingList);
                                signActualPrice = getMySignPrice(mymSignList,beginTime,yearMonth);
                            }
                        }
                        //查询版本下指定时间段的签约计划金额
                        BigDecimal signPlanPrice = signPlanVersionDetailMapper.selectSignPlanPriceByTimeHorizon(id,yearMonth,endTime,IsDeleteEnum.NO.getKey());
                        sumAmount = sumAmount.add(signActualPrice).add(signPlanPrice);
                    }else {
                        //2.2.2 调整前
                        String affiliationMonthYear = affiliationMonth.substring(0, 4);
                        //若版本归属年份与审批提交年份为同年,以版本归属时间为准,已过月份取实际,未过月份取计划
                        if(affiliationMonthYear.equals(year)){
                            //查询版本下指定时间段的签约实际金额
                            BigDecimal signActualPrice = signPlanVersionDetailMapper.selectSignActualPriceByTimeHorizon(id,beginTime,affiliationMonth,IsDeleteEnum.NO.getKey());
                            //查询版本下指定时间段的签约计划金额
                            BigDecimal signPlanPrice = signPlanVersionDetailMapper.selectSignPlanPriceByTimeHorizon(id,affiliationMonth,endTime,IsDeleteEnum.NO.getKey());
                            sumAmount = sumAmount.add(signActualPrice).add(signPlanPrice);
                        }else {
                            List ids = new ArrayList<>();
                            ids.add(id);
                            //若版本归属年份与审批提交年份不为同年,取签约版本审批提交年份的签约计划
                            BigDecimal amount = signPlanVersionDetailMapper.selectYearSigningTargetAmount(ids, year,IsDeleteEnum.NO.getKey());
                            sumAmount = sumAmount.add(amount);
                        }
                    }
                }
            }
        }
        return sumAmount;
    }



    /**
     * 计算指定时间段明源的签约金额
     * @param mymSignList 明源签约数据集合
     * @param beginTime 开始时间
     * @param endTime 结束时间
     * @return
     */
    private BigDecimal getMySignPrice(List<MymContractDto> mymSignList, String beginTime, String endTime) {

        BigDecimal signActualPrice = BigDecimal.ZERO;

        //过滤'撤销签约'的签约数据
        mymSignList = mymSignList.stream().filter(s -> !"撤销签约".equals(s.getCloseReason())).collect(Collectors.toList());

        //明源签约记录分类
        List<MymContractDto> activationList = mymSignList.stream().filter(s -> ActivationEnum.ACTIVATION.getKey().equals(s.getStatus())).collect(Collectors.toList());
        List<MymContractDto> closeList = mymSignList.stream().filter(s -> ActivationEnum.CLOSE.getKey().equals(s.getStatus())).collect(Collectors.toList());
        List<MymContractDto> compensationList = activationList.stream().filter(s ->s.getAuditDate()!=null).collect(Collectors.toList());


        Integer begin = Integer.valueOf(beginTime.replace("-", ""));
        Integer end = Integer.valueOf(endTime.replace("-", ""));

        for (MymContractDto m : activationList) {
            int ywgsDate = LocalDateTimeUtils.toIntYearMonth(m.getYwgsDate().getYear(), m.getYwgsDate().getMonthValue());
            if (ywgsDate >= begin && ywgsDate < end) {
                signActualPrice = signActualPrice.add(m.getCjRmbTotal());
            }
        }

        for (MymContractDto m : closeList) {
            int closeDate = LocalDateTimeUtils.toIntYearMonth(m.getCloseDate().getYear(), m.getCloseDate().getMonthValue());
            if (closeDate >= begin && closeDate < end) {
                signActualPrice = signActualPrice.subtract(m.getCjRmbTotal());
            }
        }
        //补差
        for (MymContractDto m : compensationList) {
            int ywgsDate = LocalDateTimeUtils.toIntYearMonth(m.getAuditDate().getYear(), m.getAuditDate().getMonthValue());
            if (ywgsDate >= begin && ywgsDate < end) {
                signActualPrice = signActualPrice.add(m.getBcTotal());
            }
        }

        return signActualPrice;
    }

    /**
     * 计算指定时间段Nos的签约金额
     * @param nosSigningList 明源签约数据集合
     * @param beginTime 开始时间
     * @param endTime 结束时间
     * @return
     */
    private BigDecimal getNosSignPrice(List<NosTradedataDto> nosSigningList, String beginTime, String endTime) {
        BigDecimal signActualPrice = BigDecimal.ZERO;

        //签约记录分类
        List<NosTradedataDto> signingList = new ArrayList<>(); //nos签约记录列表
        List<NosTradedataDto> checkOutList = new ArrayList<>(); //nos退房记录列表
        List<NosTradedataDto> compensationList = new ArrayList<>(); //nos补差记录列表
        for (NosTradedataDto nos : nosSigningList) {
            String tradeStatus = nos.getTradeStatus();
            if (tradeStatus.equals(TradeStatusEnum.SIGNING.getKey())) {
                signingList.add(nos);
            } else if (tradeStatus.equals(TradeStatusEnum.CHECK_OUT.getKey())) {
                checkOutList.add(nos);
            } else if (tradeStatus.equals(TradeStatusEnum.COMPENSATION.getKey())) {
                compensationList.add(nos);
            }
            ;
        }

        Integer begin = Integer.valueOf(beginTime.replace("-", ""));
        Integer end = Integer.valueOf(endTime.replace("-", ""));
        //签约
        for (NosTradedataDto n : signingList) {
            int tradeDate = LocalDateTimeUtils.toIntYearMonth(n.getTradeDate().getYear(), n.getTradeDate().getMonthValue());
            if (tradeDate >= begin && tradeDate < end) {
                signActualPrice = signActualPrice.add(n.getTradeMoney());
            }
        }
        //退房
        for (NosTradedataDto n : checkOutList) {
            int tradeDate = LocalDateTimeUtils.toIntYearMonth(n.getTradeDate().getYear(), n.getTradeDate().getMonthValue());
            if (tradeDate >= begin && tradeDate < end) {
                signActualPrice = signActualPrice.add(n.getTradeMoney());

            }
        }
        //补差
        for (NosTradedataDto n : compensationList) {
            int tradeDate = LocalDateTimeUtils.toIntYearMonth(n.getTradeDate().getYear(), n.getTradeDate().getMonthValue());
            if (tradeDate >= begin && tradeDate < end) {
                signActualPrice = signActualPrice.add(n.getTradeMoney());
            }
        }
        return signActualPrice;
    }


    /**
     * 全盘2019年月度指标
     */
    public List<YearEarlyBottomDto> getOverallMonthlyIndex(String projectId, int year, List<String> versionIds) {
        List<YearEarlyBottomDto> yearEarlyBottomList = new ArrayList<>();
        //签约详情
        List<SignPlanVersionDetail> spvdList = signPlanVersionDetailMapper.selectByVersionIds(versionIds, IsDeleteEnum.NO.getKey());
        List<SignPlanVersionDetail> yearSpvdList = spvdList.stream().filter(s -> s.getSignPlanTime().getYear() == year).collect(toList());
        //签约供货详情
        List<SignPlanVersionSupplyDetail> spvsdList = signPlanVersionSupplyDetailMapper.selectByTypeAndIds(versionIds, OrgTypeEunm.ORG_PRODUCT.getKey(), IsDeleteEnum.NO.getKey());
        // 取签约供货详情,有实际取实际，无实际取计划
        List<SignSupplyDetailBO> ssdBOList = switchSignSupplyDetailBO(spvsdList);
        for (int i = 1; i <=12 ; i++) {
            YearEarlyBottomDto yearEarlyBottomDto = new YearEarlyBottomDto();
            int month = i;
            yearEarlyBottomDto.setName(String.valueOf(month));
            //yyyy-MM
            String yearMonth = LocalDateTimeUtils.toStrYearMonth(year, month);
            //月度签约详情
            List<SignPlanVersionDetail> monthSpvdList = yearSpvdList.stream().filter(s -> s.getSignPlanTime().getMonthValue() == month).collect(toList());
            //月度签约供货计划详情
            List<SignPlanVersionSupplyDetail> monthSupplyPlanList = getMonthSignSupplyPlanDetail(spvsdList,yearMonth);
            //月度签约供货实际详情
            List<SignPlanVersionSupplyDetail> monthSupplyActualList = getMonthSignSupplyActualDetail(spvsdList,yearMonth);
            //月度签约供货详情
            List<SignSupplyDetailBO> monthSupplyList = ssdBOList.stream().filter(s -> LocalDateTimeUtils.getLocalMonth(s.getSupplyDate()).equals(yearMonth)).collect(Collectors.toList());

            ////////////////////////////////////////// 月初存货///////////////////////////////////////////////////
            BigDecimal f_monthlyInventoryNumH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getEarlyRoomCount()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_monthlyInventoryNumC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getEarlyRoomCount()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_monthlyInventoryAmountH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getEarlySumPrice()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_monthlyInventoryAmountC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getEarlySumPrice()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_signingPlanNumH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getEarlySignRoomCount()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_signingPlanNumC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getEarlySignRoomCount()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_signingPlanAmountH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getEarlySignSumPrice()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_signingPlanAmountC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getEarlySignSumPrice()).reduce(BigDecimal.ZERO,BigDecimal::add);
            Integer f_signingActualNumH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).mapToInt(s -> s.getEarlySignRoomCountActual()).sum();
            Integer f_signingActualNumC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).mapToInt(s -> s.getEarlySignRoomCountActual()).sum();
            BigDecimal f_signingActualAmountH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getEarlySignSumPriceActual()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal f_signingActualAmountC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getEarlySignSumPriceActual()).reduce(BigDecimal.ZERO,BigDecimal::add);
            /////////////////////////////////////////////月度新供///////////////////////////////////////////////
            Integer g_supplyNumH = monthSupplyList.stream().filter(s ->s.getIsParking() == 0).mapToInt(s -> s.getRoomNum()).sum();
            Integer g_supplyNumC = monthSupplyList.stream().filter(s ->s.getIsParking() == 1).mapToInt(s -> s.getParkingNum()).sum();
            BigDecimal g_supplyAmountH = monthSupplyList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getHouseCommodityValue()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_supplyAmountC = monthSupplyList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getNotCommodityValue()).reduce(BigDecimal.ZERO,BigDecimal::add);
            Integer g_supplyPlanNumH = monthSupplyPlanList.stream().filter(s ->s.getIsParking() == 0).mapToInt(s -> s.getRoomNum()).sum();
            Integer g_supplyPlanNumC = monthSupplyPlanList.stream().filter(s ->s.getIsParking() == 1).mapToInt(s -> s.getParkingNum()).sum();
            BigDecimal g_supplyPlanAmountH = monthSupplyPlanList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getHouseCommodityValue()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_supplyPlanAmountC = monthSupplyPlanList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getNotCommodityValue()).reduce(BigDecimal.ZERO,BigDecimal::add);
            Integer g_supplyPlanActualNumH = monthSupplyActualList.stream().filter(s ->s.getIsParking() == 0).mapToInt(s -> s.getRoomNumActual()).sum();
            Integer g_supplyPlanActualNumC = monthSupplyActualList.stream().filter(s ->s.getIsParking() == 1).mapToInt(s -> s.getParkingNumActual()).sum();
            BigDecimal g_supplyPlanActualAmountH = monthSupplyActualList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getHouseCommodityValueActual()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_supplyPlanActualAmountC = monthSupplyActualList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getNotCommodityValueActual()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_signingPlanNumH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getSupplySignRoomCount()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_signingPlanNumC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getSupplySignRoomCount()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_signingPlanAmountH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getSupplySignSumPrice()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_signingPlanAmountC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getSupplySignSumPrice()).reduce(BigDecimal.ZERO,BigDecimal::add);
            Integer g_signingActualNumH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).mapToInt(s -> s.getSupplySignRoomCountActual()).sum();
            Integer g_signingActualNumC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).mapToInt(s -> s.getSupplySignRoomCountActual()).sum();
            BigDecimal g_signingActualAmountH = monthSpvdList.stream().filter(s ->s.getIsParking() == 0).map(s -> s.getSupplySignSumPriceActual()).reduce(BigDecimal.ZERO,BigDecimal::add);
            BigDecimal g_signingActualAmountC = monthSpvdList.stream().filter(s ->s.getIsParking() == 1).map(s -> s.getSupplySignSumPriceActual()).reduce(BigDecimal.ZERO,BigDecimal::add);

            /* 月初存货套数 */
            String f_monthlyInventoryNum = BigDecimalUtils.houseAddParking(f_monthlyInventoryNumH,f_monthlyInventoryNumC,"num");
            /* 月初存货金额 */
            String f_monthlyInventoryAmount = BigDecimalUtils.houseAddParking(f_monthlyInventoryAmountH,f_monthlyInventoryAmountC,"amount");
            /* 月初存货签约计划套数 */
            String f_signingPlanNum = BigDecimalUtils.houseAddParking(f_signingPlanNumH,f_signingPlanNumC,"num");
            /* 月初存货签约计划金额 */
            String f_signingPlanAmount = BigDecimalUtils.houseAddParking(f_signingPlanAmountH,f_signingPlanAmountC,"amount");
            /* 月初存货签约实际套数 */
            String f_signingActualNum = BigDecimalUtils.houseAddParking(new BigDecimal(f_signingActualNumH),new BigDecimal(f_signingActualNumC),"num");
            /* 月初存货签约实际金额 */
            String f_signingActualAmount = BigDecimalUtils.houseAddParking(f_signingActualAmountH,f_signingActualAmountC,"amount");
            /* 月初存货签约达成率(月初存货签约实际金额/月初存货签约计划金额) */
            String f_signingRateAmount = NumberUtils.pricePercentage(f_signingActualAmount, f_signingPlanAmount);
            /* 月初存货计划去化率(月初存货签约计划/月初存货) */
            String f_planDeChemicalRateAmount = NumberUtils.pricePercentage(f_signingPlanAmount, f_monthlyInventoryAmount);


            /* 月度新供供货套数 */
            String g_supplyNum = BigDecimalUtils.houseAddParking(new BigDecimal(g_supplyNumH),new BigDecimal(g_supplyNumC),"num");
            /* 月度新供供货金额 */
            String g_supplyAmount = BigDecimalUtils.houseAddParking(g_supplyAmountH,g_supplyAmountC,"amount");
            /* 月度新供供货计划套数 */
            String g_supplyPlanNum = BigDecimalUtils.houseAddParking(new BigDecimal(g_supplyPlanNumH),new BigDecimal(g_supplyPlanNumC),"num");
            /* 月度新供供货计划金额 */
            String g_supplyPlanAmount = BigDecimalUtils.houseAddParking(g_supplyPlanAmountH,g_supplyPlanAmountC,"amount");
            /* 月度新供供货实际套数 */
            String g_supplyPlanActualNum = BigDecimalUtils.houseAddParking(new BigDecimal(g_supplyPlanActualNumH),new BigDecimal(g_supplyPlanActualNumC),"num");
            /* 月度新供供货实际金额 */
            String g_supplyPlanActualAmount = BigDecimalUtils.houseAddParking(g_supplyPlanActualAmountH,g_supplyPlanActualAmountC,"amount");
            /* 月度新供签约计划套数 */
            String g_signingPlanNum = BigDecimalUtils.houseAddParking(g_signingPlanNumH,g_signingPlanNumC,"num");
            /* 月度新供签约计划金额 */
            String g_signingPlanAmount = BigDecimalUtils.houseAddParking(g_signingPlanAmountH,g_signingPlanAmountC,"amount");
            /* 月度新供签约实际套数 */
            String g_signingActualNum = BigDecimalUtils.houseAddParking(new BigDecimal(g_signingActualNumH),new BigDecimal(g_signingActualNumC),"num");
            /* 月度新供签约实际金额 */
            String g_signingActualAmount = BigDecimalUtils.houseAddParking(g_signingActualAmountH,g_signingActualAmountC,"amount");
            /* 月度供货达成率(月度新供供货实际金额/月度新供供货计划金额) */
            String g_supplyPlanRateAmount = NumberUtils.pricePercentage(g_supplyPlanActualAmount, g_supplyPlanAmount);
            /* 月度新供签约达成率(月度新供签约实际金额/月度新供签约计划金额) */
            String g_signingRateAmount = NumberUtils.pricePercentage(g_signingActualAmount,g_signingPlanAmount);
            /* 月度新供计划去化率(月度新供签约计划金额/月度新供供货计划金额) */
            String g_planDeChemicalRateAmount = NumberUtils.pricePercentage(g_signingPlanAmount,g_supplyPlanAmount);

            // 月度综合月度可售套数(月初存货套数+月度新供供货套数)
            String h_monthlyAvailableNum = NumberUtils.numAdd(f_monthlyInventoryNum, g_supplyNum);
            // 月度综合月度可售金额(月初存货金额+月度新供供货金额)
            String h_monthlyAvailableAmount = NumberUtils.priceAdd(f_monthlyInventoryAmount, g_supplyAmount);
            // 月度综合签约计划套数(月度新供签约计划套数+月初存货签约计划套数)
            String h_signingPlanNum = NumberUtils.numAdd(g_signingPlanNum, f_signingPlanNum);
            // 月度综合签约计划金额(月度新供签约计划金额+月初存货签约计划金额)
            String h_signingPlanAmount = NumberUtils.priceAdd(g_signingPlanAmount, f_signingPlanAmount);
            // 月度综合签约实际套数(月度新供签约实际套数+月初存货签约实际套数)
            String h_signingActualNum = NumberUtils.numAdd(g_signingActualNum, f_signingActualNum);
            // 月度综合签约实际金额(月度新供签约实际金额+月初存货签约实际金额)
            String h_signingActualAmount = NumberUtils.priceAdd(g_signingActualAmount, f_signingActualAmount);
            /* 月度综合签约达成率(月度综合签约实际金额/月度综合签约计划金额) */
            String h_signingRateAmount = NumberUtils.pricePercentage(h_signingActualAmount,h_signingPlanAmount);
            // 月度综合计划去化率(月度综合签约计划/月度可售)
            String h_planDeChemicalRateAmount = NumberUtils.pricePercentage(h_signingPlanAmount, h_monthlyAvailableAmount);

            yearEarlyBottomDto.setF_monthlyInventoryNum(f_monthlyInventoryNum);
            yearEarlyBottomDto.setF_monthlyInventoryAmount(f_monthlyInventoryAmount);
            yearEarlyBottomDto.setF_signingPlanNum(f_signingPlanNum);
            yearEarlyBottomDto.setF_signingPlanAmount(f_signingPlanAmount);
            yearEarlyBottomDto.setF_signingActualNum(f_signingActualNum);
            yearEarlyBottomDto.setF_signingActualAmount(f_signingActualAmount);
            yearEarlyBottomDto.setF_signingRateAmount(f_signingRateAmount);
            yearEarlyBottomDto.setF_planDeChemicalRateAmount(f_planDeChemicalRateAmount);
            yearEarlyBottomDto.setG_supplyPlanNum(g_supplyPlanNum);
            yearEarlyBottomDto.setG_supplyPlanAmount(g_supplyPlanAmount);
            yearEarlyBottomDto.setG_supplyPlanActualNum(g_supplyPlanActualNum);
            yearEarlyBottomDto.setG_supplyPlanActualAmount(g_supplyPlanActualAmount);
            yearEarlyBottomDto.setG_supplyPlanRateAmount(g_supplyPlanRateAmount);
            yearEarlyBottomDto.setG_signingPlanNum(g_signingPlanNum);
            yearEarlyBottomDto.setG_signingPlanAmount(g_signingPlanAmount);
            yearEarlyBottomDto.setG_signingActualNum(g_signingActualNum);
            yearEarlyBottomDto.setG_signingActualAmount(g_signingActualAmount);
            yearEarlyBottomDto.setG_signingRateAmount(g_signingRateAmount);
            yearEarlyBottomDto.setG_planDeChemicalRateAmount(g_planDeChemicalRateAmount);
            yearEarlyBottomDto.setH_monthlyAvailableNum(h_monthlyAvailableNum);
            yearEarlyBottomDto.setH_monthlyAvailableAmount(h_monthlyAvailableAmount);
            yearEarlyBottomDto.setH_signingPlanNum(h_signingPlanNum);
            yearEarlyBottomDto.setH_signingPlanAmount(h_signingPlanAmount);
            yearEarlyBottomDto.setH_signingActualNum(h_signingActualNum);
            yearEarlyBottomDto.setH_signingActualAmount(h_signingActualAmount);
            yearEarlyBottomDto.setH_signingRateAmount(h_signingRateAmount);
            yearEarlyBottomDto.setH_planDeChemicalRateAmount(h_planDeChemicalRateAmount);

            yearEarlyBottomList.add(yearEarlyBottomDto);
        }

        //单位转换
        for (YearEarlyBottomDto yebd : yearEarlyBottomList) {
            yebd.setF_monthlyInventoryNum(NumberUtils.updateBigAccuracy(yebd.getF_monthlyInventoryNum(),2));
            yebd.setF_monthlyInventoryAmount(NumberUtils.priceToWanAndMicrometer(yebd.getF_monthlyInventoryAmount()));
            yebd.setF_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getF_signingPlanNum(),2));
            yebd.setF_signingPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getF_signingPlanAmount()));
            yebd.setF_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getF_signingActualNum(),2));
            yebd.setF_signingActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getF_signingActualAmount()));
            yebd.setG_supplyPlanNum(NumberUtils.updateBigAccuracy(yebd.getG_supplyPlanNum(),2));
            yebd.setG_supplyPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_supplyPlanAmount()));
            yebd.setG_supplyPlanActualNum(NumberUtils.updateBigAccuracy(yebd.getG_supplyPlanActualNum(),2));
            yebd.setG_supplyPlanActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_supplyPlanActualAmount()));
            yebd.setG_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getG_signingPlanNum(),2));
            yebd.setG_signingPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_signingPlanAmount()));
            yebd.setG_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getG_signingActualNum(),2));
            yebd.setG_signingActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getG_signingActualAmount()));
            yebd.setH_monthlyAvailableNum(NumberUtils.updateBigAccuracy(yebd.getH_monthlyAvailableNum(),2));
            yebd.setH_monthlyAvailableAmount(NumberUtils.priceToWanAndMicrometer(yebd.getH_monthlyAvailableAmount()));
            yebd.setH_signingPlanNum(NumberUtils.updateBigAccuracy(yebd.getH_signingPlanNum(),2));
            yebd.setH_signingPlanAmount(NumberUtils.priceToWanAndMicrometer(yebd.getH_signingPlanAmount()));
            yebd.setH_signingActualNum(NumberUtils.updateBigAccuracy(yebd.getH_signingActualNum(),2));
            yebd.setH_signingActualAmount(NumberUtils.priceToWanAndMicrometer(yebd.getH_signingActualAmount()));
        }
        return yearEarlyBottomList;
    }

    /**
     * 月度签约供货实际详情
     * @param spvsdList
     * @param yearMonth
     * @return
     */
    private List<SignPlanVersionSupplyDetail> getMonthSignSupplyActualDetail(List<SignPlanVersionSupplyDetail> spvsdList, String yearMonth) {
        List<SignPlanVersionSupplyDetail> list = new ArrayList<>();

        for (SignPlanVersionSupplyDetail spvsd : spvsdList) {

            LocalDateTime supplyActualDate;
            if(null != spvsd.getSupplyTimeVirtual()){
                //虚拟拿证时间
                supplyActualDate = spvsd.getSupplyTimeVirtual();
            }else {
                //实际供货时间
                supplyActualDate = spvsd.getSupplyDateActual();
            }
            if (null == supplyActualDate ) {
                continue;
            }
            //实际供货年月
            String supplyActualTime = LocalDateTimeUtils.formatTime(supplyActualDate, "yyyy-MM");
            if (yearMonth.equals(supplyActualTime)) {
                list.add(spvsd);
            }
        }
        return list;
    }

    /**
     * 月度签约供货计划详情
     * @param spvsdList
     * @param yearMonth
     * @return
     */
    private List<SignPlanVersionSupplyDetail> getMonthSignSupplyPlanDetail(List<SignPlanVersionSupplyDetail> spvsdList, String yearMonth) {
        List<SignPlanVersionSupplyDetail> list = new ArrayList<>();
        for (SignPlanVersionSupplyDetail spvsd: spvsdList){
            //计划供货时间
            LocalDateTime supplyPlanDate = spvsd.getSupplyDate();
            String supplyPlanTime = LocalDateTimeUtils.formatTime(supplyPlanDate, "yyyy-MM");
            if (yearMonth.equals(supplyPlanTime)) {
                if (spvsd.getSupplyDateActual() != null || spvsd.getSupplyTimeVirtual() != null) {
                    LocalDateTime supplyActualDate;
                    if (null != spvsd.getSupplyTimeVirtual()) {
                        supplyActualDate = spvsd.getSupplyTimeVirtual();
                    } else {
                        supplyActualDate = spvsd.getSupplyDateActual();
                    }
                    int yearMonthPlan = LocalDateTimeUtils.toIntYearMonth(supplyPlanDate.getYear(), supplyPlanDate.getMonthValue());
                    int yearMonthActual = LocalDateTimeUtils.toIntYearMonth(supplyActualDate.getYear(), supplyActualDate.getMonthValue());
                    //如当月之前已经实际供货，不参与计算
                    if (yearMonthActual < yearMonthPlan) {
                        continue;
                    }
                }
                list.add(spvsd);
            }
        }
        return list;
    }
    /**
     * 月均销售同比偏差
     * @param versionIds 全盘签约版本ids
     * @param yearMonth 提交审批时间
     * @return
     */
    public List<MonthSaleRateDeviationDto> getMonthAvgSaleRateDeviation(List<String> versionIds, String yearMonth) {
        List<MonthSaleRateDeviationDto> list = new ArrayList<>();
        //当年
        String year = yearMonth.substring(0, 4);
        //去年
        String lastYear = Integer.valueOf((Integer.valueOf(year) - 1)).toString();
        String yearFirstMonth = year + "-01";
        // 去年第一个月
        String lastYearFirstMonth = lastYear + "-01";

        //查询版本下产品构成Code列表
        List<SignPlanVersionDetail> productList = signPlanVersionDetailMapper.selectCodeById(versionIds, IsDeleteEnum.NO.getKey());
        for (SignPlanVersionDetail product : productList) {
            MonthSaleRateDeviationDto dto = new MonthSaleRateDeviationDto();
            //业态参数
            SignProductCodeBO signProductCodeBO = new SignProductCodeBO();
            String productCode = product.getProductCode();
            String businessType = product.getBusinessType();
            String freeType = product.getFreeType();
            String proType = product.getProType();
            signProductCodeBO.setProductCode(productCode);
            signProductCodeBO.setBusinessType(businessType);
            signProductCodeBO.setFreeType(freeType);
            signProductCodeBO.setProType(proType);
            signProductCodeBO.setIsDel(IsDeleteEnum.NO.getKey());

            List<SignPlanVersionDetail> spvdList = new ArrayList<>();
            List<SignPlanVersionSupplyDetail> spvsdList = new ArrayList<>();
            //当年年度签约目标
            BigDecimal yearSignTarget = BigDecimal.ZERO;
            for (String vrsionId : versionIds){
                signProductCodeBO.setVersionId(vrsionId);
                //查询业态签约详情数据
                List<SignPlanVersionDetail> spvds = signPlanVersionDetailMapper.selectByCode(signProductCodeBO);
                //查询业态签约供货详情数据
                List<SignPlanVersionSupplyDetail> spvsds = signPlanVersionSupplyDetailMapper.selectByCode(signProductCodeBO);
                //计算版本下业态的当年签约目标
                BigDecimal amount = getCodeYearSignTarget(vrsionId,yearMonth,spvds,spvsds);
                yearSignTarget = yearSignTarget.add(amount);

                spvdList.addAll(spvds);
                spvsdList.addAll(spvsds);
            }
            //当年签约详情数据
            List<SignPlanVersionDetail> yearSpvdList = spvdList.stream().filter(s -> year.equals(String.valueOf(s.getSignPlanTime().getYear()))).collect(Collectors.toList());
            //去年签约详情数据
            List<SignPlanVersionDetail> lastYearSpvdList = spvdList.stream().filter(s -> lastYear.equals(String.valueOf(s.getSignPlanTime().getYear()))).collect(Collectors.toList());
            //去年签约供货详情数据
            List<SignPlanVersionSupplyDetail> lastYearSpvsdList = spvsdList.stream().filter(s -> lastYear.equals(String.valueOf(s.getSupplyDateActual() == null ?"" : s.getSupplyDateActual().getYear()))).collect(Collectors.toList());

            //当年月度签约金额不为0的月数
            int monthCount = 0;
            //当年年初存货金额
            BigDecimal yearEalyAvailableAmout = BigDecimal.ZERO;
            //当年年度新供金额
            BigDecimal yearSupplyAmout = BigDecimal.ZERO;
            //当年可售金额（当年年初存货金额 + 当年年度新供金额）
            BigDecimal yearAvailableAmout = BigDecimal.ZERO;
            if(CollectionUtils.isNotEmpty(yearSpvdList)){
                for (int i = 1; i <=12 ; i++) {
                    int month = i;
                    int size = yearSpvdList.stream().filter(s -> s.getSignPlanTime().getMonthValue() == month
                            && (s.getEarlySignSumPrice().compareTo(BigDecimal.ZERO) != 0
                            || s.getSupplySignSumPrice().compareTo(BigDecimal.ZERO) != 0))
                            .collect(Collectors.toList()).size();
                    if(size > 0){
                        monthCount++;
                    }
                }
                List<SignPlanVersionDetail> yearEalyList = yearSpvdList.stream().filter(s -> LocalDateTimeUtils.getLocalMonth(s.getSignPlanTime()).equals(yearFirstMonth)).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(yearEalyList)){
                    for (SignPlanVersionDetail detail : yearEalyList){
                        yearEalyAvailableAmout = yearEalyAvailableAmout.add(detail.getEarlySumPrice());
                    }
                }
                for (SignPlanVersionDetail detail : yearSpvdList){
                    yearSupplyAmout = yearSupplyAmout.add(detail.getSupplySumPrice());
                }
                yearAvailableAmout = yearEalyAvailableAmout.add(yearSupplyAmout);
            }

            //去年实际销售金额
            BigDecimal lastYearSingActualAmout = BigDecimal.ZERO;
            //去年实际销售的金额不为0的月数
            int lastYearMonthCount = 0;
            //去年年初存货金额
            BigDecimal lastYearEalyAvailableAmout = BigDecimal.ZERO;
            if(CollectionUtils.isNotEmpty(lastYearSpvdList)){
                for(SignPlanVersionDetail detail : lastYearSpvdList){
                    //去年实际销售金额 = 月初存货实际销售 + 月度新供实际销售
                    lastYearSingActualAmout = lastYearSingActualAmout.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
                }

                for (int i = 1; i <=12 ; i++) {
                    int month = i;
                    //指定月份,月初存货金额或本月新供实际签约金额不为0的数据条数
                    int size = lastYearSpvdList.stream().filter(s -> s.getSignPlanTime().getMonthValue() == month && (s.getEarlySignSumPriceActual().compareTo(BigDecimal.ZERO) != 0 || s.getSupplySignSumPriceActual().compareTo(BigDecimal.ZERO) != 0)).collect(Collectors.toList()).size();
                    if(size > 0){
                        lastYearMonthCount++;
                    }
                }
                List<SignPlanVersionDetail> lastYearEalyList = lastYearSpvdList.stream().filter(s -> LocalDateTimeUtils.getLocalMonth(s.getSignPlanTime()).equals(lastYearFirstMonth)).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(lastYearEalyList)){
                    for(SignPlanVersionDetail detail : lastYearEalyList){
                        lastYearEalyAvailableAmout = lastYearEalyAvailableAmout.add(detail.getEarlySumPrice());
                    }
                }
            }
            //去年年度实际新供金额
            BigDecimal lastYearSupplyAmout = BigDecimal.ZERO;
            if(CollectionUtils.isNotEmpty(lastYearSpvsdList)){
                for(SignPlanVersionSupplyDetail detail : lastYearSpvsdList){
                    lastYearSupplyAmout = lastYearSupplyAmout.add(detail.getHouseCommodityValueActual().add(detail.getNotCommodityValueActual()));
                }
            }
            //去年实际可售（去年年初存货 + 去年年度实际供货）
            BigDecimal lastYearAvailableAmout = lastYearEalyAvailableAmout.add(lastYearSupplyAmout);

            //业态名称
            String commercial = product.getProductName() + "+" + product.getBusinessTypeName() + "+" + product.getFreeTypeName() + "+" + product.getProTypeName();
            //当年月均销售 = 当年年度签约目标 / 当年月度签约金额不为0的月数
            BigDecimal thatYearMonthSaleAmount = BigDecimalUtils.divInteger(yearSignTarget,monthCount,4);
            //当年综合去化率 = 当年年度签约目标 / 当年可售金额（当年年初存货 + 当年年度新供）
            String thatYearTotalRate = StrUtils.rateAchievement(yearSignTarget,yearAvailableAmout);
            //去年月均销售 = 去年实际销售 / 去年实际销售的金额不为0的月数
            BigDecimal lastYearMonthSaleAmount = BigDecimalUtils.divInteger(lastYearSingActualAmout,lastYearMonthCount,4);;
            //去年综合去化率 = 去年实际销售 / （去年年初存货 + 年度实际供货）
            String lastYearTotalRate = StrUtils.rateAchievement(lastYearSingActualAmout,lastYearAvailableAmout);
            //偏差金额 = 当年月均销售 - 去年月均销售
            BigDecimal monthSaleRateDeviationAmount = thatYearMonthSaleAmount.subtract(lastYearMonthSaleAmount);
            //偏差比例 = 偏差金额 / 去年月均销售
            String monthSaleRateDeviationRate = StrUtils.rateAchievement(monthSaleRateDeviationAmount,lastYearMonthSaleAmount);

            dto.setCommercial(commercial);
            dto.setThatYearMonthSaleAmount(thatYearMonthSaleAmount.toString());
            dto.setLastYearMonthSaleAmount(lastYearMonthSaleAmount.toString());
            dto.setMonthSaleRateDeviationAmount(monthSaleRateDeviationAmount.toString());
            dto.setThatYearTotalRate(thatYearTotalRate);
            dto.setLastYearTotalRate(lastYearTotalRate);
            dto.setMonthSaleRateDeviationRate(monthSaleRateDeviationRate.replace("%", ""));
            list.add(dto);
        }
        return list;
    }


    /**
     * 获取业态年度签约目标
     * 1、非预算版(战规、动态: 已过月份取实际,未过月份取计划;若是投资版: 全取计划)
     * 2、预算版（查看来源版本类型,参考1）
     * @param versionId 版本id
     * @param yearMonth 审批发起时间
     * @param spvdList
     * @param spvsdList
     * @return
     */
    private BigDecimal getCodeYearSignTarget(String versionId, String yearMonth, List<SignPlanVersionDetail> spvdList, List<SignPlanVersionSupplyDetail> spvsdList) {
        BigDecimal amount = BigDecimal.ZERO;
        String year = yearMonth.substring(0, 4);
        String beginTime = year + "-01";
        Integer yearMonthInt = Integer.valueOf(yearMonth.replace("-", ""));
        List<SignPlanVersionDetail> spvds = new ArrayList<>();

        //当年业态签约详情数据
        List<SignPlanVersionDetail> yearSpvdList = spvdList.stream().filter(s -> year.equals(String.valueOf(s.getSignPlanTime().getYear()))).collect(Collectors.toList());

        //查询版本
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectById(versionId);
        //版本属性
        String versionAttr = signPlanVersion.getVersionAttr();

        //1.若是投资版，全取计划
        if(VersionAttrEnum.BEFORE_WARPLAN.getKey().equals(versionAttr)){
            if(CollectionUtils.isNotEmpty(yearSpvdList)) {
                for(SignPlanVersionDetail detail : yearSpvdList){
                    amount =  amount.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                }
            }
        }else {
            BigDecimal planAmount = BigDecimal.ZERO;
            BigDecimal actualAmount = BigDecimal.ZERO;

            //2.战规版或动态版已过月份取实际,未过月份取计划
            String affiliationMonth = signPlanVersion.getAffiliationMonth();
            //2.1 若版本创建时间等于版本提交审批时间,说明在签约数据在本地表中有存储,需要查询本地表数据
            if(yearMonth.equals(affiliationMonth)){
                //当年未过月份签约计划金额
                spvds = yearSpvdList.stream().filter(s -> yearMonthInt <= LocalDateTimeUtils.toIntYM(s.getSignPlanTime())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(spvds)) {
                    for (SignPlanVersionDetail detail:spvds){
                        planAmount = planAmount.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                    }
                }
                //当年已过月份签约实际金额
                spvds = yearSpvdList.stream().filter(s -> yearMonthInt > LocalDateTimeUtils.toIntYM(s.getSignPlanTime())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(spvds)) {
                    for (SignPlanVersionDetail detail:spvds){
                        actualAmount = actualAmount.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
                    }
                }
                amount = planAmount.add(actualAmount);
            }else {
                //2.2 若不相等,视为跨月审批,以版本提交审批时间为准,已过月份取实际,未过月份取计划,需要查询明源或Nos视图表数据
                String projectfId = signPlanVersion.getProjectfId();
                //查询签约视图取数系统
                SysProjectFetchNumber sysProjectFetchNumber = sysProjectFetchNumberService.selectByFid(projectfId);
                String countingSystemId = sysProjectFetchNumber.getCountingSystemId();
                //查询版本下全部楼栋id
                List<String> buildingList = spvsdList.stream().map(s ->s.getOrgId()).collect(Collectors.toList());

                //计算签约实际数据
                List<NosTradedataDto> nosSigningList = new ArrayList<>();
                List<MymContractDto> mymSignList = new ArrayList<>();

                if(CollectionUtils.isNotEmpty(buildingList)){
                    if (countingSystemId.equals(FetchNnumberEnum.NOS.getKey())) {
                        //查询指定楼栋中全部nos签约数据
                        nosSigningList = dmOdsNosTradedataviewformarsService.selectNosContractList(buildingList);
                        actualAmount = getNosSignPrice(nosSigningList,beginTime,yearMonth);
                    } else if (countingSystemId.equals(FetchNnumberEnum.MY.getKey())) {
                        //查询指定楼栋中全部明源签约数据
                        mymSignList = dmOdsMyVsMarsSContractService.selectMymContractList(buildingList);
                        actualAmount = getMySignPrice(mymSignList,beginTime,yearMonth);
                    }
                }else {
                    actualAmount = BigDecimal.ZERO;
                }

                //查询版本下指定时间段的签约计划金额
                //当年未过月份签约计划金额
                spvds = yearSpvdList.stream().filter(s -> yearMonthInt <= LocalDateTimeUtils.toIntYM(s.getSignPlanTime())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(spvds)) {
                    for (SignPlanVersionDetail detail : spvds){
                        planAmount = planAmount.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                    }
                }
                amount = actualAmount.add(planAmount);
            }
        }
        return amount;
    }
    /**
     * 全盘查询当月到12月的签约计划金额
     *
     * @return
     */
    public BigDecimal getLocalMonthAndAfterSignPlanAmount(List<String> versionIds, String beginTime, String endTime) {
        String amount = signPlanVersionDetailMapper.selectLocalMonthAndAfterSignPlanAmount(versionIds, beginTime,
                endTime, IsDeleteEnum.NO.getKey());
        return new BigDecimal(amount);
    }

    /**
     * 全盘签约计划实际金额 大于等于1月,小于当前月
     *
     * @param versionIds 版本id列表
     * @param beginTime  开始时间
     * @param endTime    结束时间
     * @return
     */
    public BigDecimal getSignPlanActualAmount(List<String> versionIds, String beginTime, String endTime) {
        if (versionIds.size() == 0) {
            return new BigDecimal(0);
        }
        String amount = signPlanVersionDetailMapper.selectSignPlanActualAmount(versionIds, IsDeleteEnum.NO.getKey(),
                beginTime, endTime);
        return new BigDecimal(amount);
    }

    /**
     *  计算年初全盘未供货值
     */
    public BigDecimal getEarlyYearWholesaleSupplyAmount(List<String> versionIds, String year) {
        BigDecimal sumAmount = BigDecimal.ZERO;

        int yearInt = Integer.valueOf(year);

        //投资版版本列表
        List <String> investmentList = new ArrayList<>();
        //非投资版版本列表
        List <String> notInvestmentList = new ArrayList<>();
        //查询签约版本列表
        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.selectByVersionIds(versionIds, IsDeleteEnum.NO.getKey());
        for (SignPlanVersion signPlanVersion : signPlanVersionList) {
            if (signPlanVersion.getVersionType().equals(VersionTypeEunm.INVESTMENT.getKey())) {
                investmentList.add(signPlanVersion.getId());
            } else {
                notInvestmentList.add(signPlanVersion.getId());
            }
        }

        if(CollectionUtils.isNotEmpty(investmentList)){
            sumAmount = signPlanVersionSupplyDetailMapper.selectTotalValue(investmentList, IsDeleteEnum.NO.getKey());
        }
        if(CollectionUtils.isNotEmpty(notInvestmentList)){
            //供货详情
            List<SignPlanVersionSupplyDetail> signSupplyDetailList = signPlanVersionSupplyDetailMapper.selectByTypeAndIds(notInvestmentList, OrgTypeEunm.ORG_PRODUCT.getKey(), IsDeleteEnum.NO.getKey());
            //年初存货未供详情
            List<SignPlanVersionSupplyDetail> earlyYearWholesaleSupplyList = signSupplyDetailList.stream().filter( s ->
                (
                    (s.getSupplyTimeVirtual() != null && s.getSupplyTimeVirtual().getYear() >= yearInt) ||
                    (s.getSupplyTimeVirtual() == null && s.getSupplyDateActual() != null && s.getSupplyDateActual().getYear() >= yearInt) ||
                    (s.getSupplyTimeVirtual() == null && s.getSupplyDateActual() == null &&s.getSupplyDate() != null && s.getSupplyDate().getYear() >= yearInt)
                )
            ).collect(Collectors.toList());

            BigDecimal earlyYearWholesaleSupplyAmount = BigDecimal.ZERO;
            //取总货值
            if(CollectionUtils.isNotEmpty(earlyYearWholesaleSupplyList)){
                earlyYearWholesaleSupplyAmount = earlyYearWholesaleSupplyList.stream().map(s -> s.getHouseCommodityValue().add(s.getNotCommodityValue())).reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            sumAmount = sumAmount.add(earlyYearWholesaleSupplyAmount);
        }
        return sumAmount;
    }



    /**
     * 根据版本类型和分期id 获取最新审批通过的 当年年度计划签约金额
     */
    public BigDecimal getSignPlanTypeAmount(List<String> versionIds, String year) {
        // 根据版本id查询签约计划金额
        if (versionIds.size() == 0) {
            return new BigDecimal(0);
        }
        BigDecimal amount = signPlanVersionDetailMapper.selectYearSigningTargetAmount(versionIds,year,IsDeleteEnum.NO.getKey());
        return amount;
    }


    /**
     * 查询签约计划时间条数
     *
     * @return
     */
    public int getSignPlanTimeNum(List<String> versionIds, String beginTime, String endTime, String productCode
    		,String businessTypeName,String freeTypeName,String proTypeName) {
        List<MonthAvgSaleRateDeviationDto> list = signPlanVersionDetailMapper.selectTimeAmount(versionIds,
                IsDeleteEnum.NO.getKey(), beginTime, endTime, productCode,businessTypeName,freeTypeName,proTypeName);
        int num = 0;
        if (list.size() > 0) {
            for (MonthAvgSaleRateDeviationDto x : list) {
                double amount = Double.parseDouble(x.getQyPlanAmount());
                if (amount > 0) {
                    num++;
                }
            }
        }
        return num;
    }

    /**
     * 获取实际签约的条数
     *
     * @return
     */
    public int getActualSignPlanNum(List<String> versionIds, String productCode, String beginTime, String endTime,
    		String businessTypeName,String freeTypeName,String proTypeName) {
        List<MonthAvgSaleRateDeviationDto> list = signPlanVersionDetailMapper.selectSignPlanActualInfo(versionIds,
                IsDeleteEnum.NO.getKey(), beginTime, endTime, productCode,businessTypeName,freeTypeName,proTypeName);
        int num = 0;
        if (list.size() > 0) {
            for (MonthAvgSaleRateDeviationDto x : list) {
                double amount = Double.parseDouble(x.getQyActualAmount());
                if (amount > 0) {
                    num++;
                }
            }
        }
        return num;
    }

    /**
     * 获取去年的实际签约的条数
     *
     * @return
     */
    public int getYearActualSignPlanNum(List<String> versionIds, String productCode, String year
    		,String businessTypeName,String freeTypeName,String proTypeName) {
        String lastYear = Integer.parseInt(year) - 1 + "";
        List<MonthAvgSaleRateDeviationDto> list = signPlanVersionDetailMapper.selectYearActualSignPlanInfo(versionIds,
                IsDeleteEnum.NO.getKey(), lastYear, productCode,businessTypeName,freeTypeName,proTypeName);
        int num = 0;
        if (list.size() > 0) {
            for (MonthAvgSaleRateDeviationDto x : list) {
                double amount = Double.parseDouble(x.getQyActualAmount());
                if (amount > 0) {
                    num++;
                }
            }
        }
        return num;
    }

    /**
     * 提交到OA审批-》签约审批
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public String submitToOA(SignPlanOaParam param, IdmUser user) {
        String businessType = param.getBusinessType();
        String businessKey = param.getBusinessKey();
        String description = param.getDescription();
        List<String> files = param.getFiles();

        //审批分支变量 版本阶段
        String versionTypeCode = param.getVersionTypeCode();
        //全盘签约计划id集合
        List<String> overallVersionIds = param.getOverallVersionIds();
        //对比差异版本id集合
        List<SignPlanEdition> signPlanEditioList = param.getPkVersionIds();
        //本次编制的版本id集合
        List<String> organizationIds = param.getOrganizationIds();
        //编制的年月
        String yearMonth = param.getYearMonth();
        //项目id
        String projectId = param.getProjectId();

        ApprovalBill approvalBill = null;
        if (StringUtils.isNotBlank(businessKey)) {
            //根据审批单id获取项目id
            approvalBill = approvalBillMapper.selectById(businessKey);
            if (approvalBill.getState().equals(ApprovedStateCodeEnum.DELETE.getKey())
                    || approvalBill.getState().equals(ApprovedStateCodeEnum.ENDFLOW.getKey())){
                throw new MsgException(CodeEnum.LOCKBILL_CANNOT_SUBMIT);
            }
            if (approvalBill != null) {
                projectId = approvalBill.getProjectId();
            }
        }
        //查询是否可以提交审批
        checkSubmitApproval(projectId);
        if (CollectionUtils.isNotEmpty(organizationIds)) {
            //判断签约版本中是否存在未编制完成的数据
            boolean isEstablishment = isEstablishment(organizationIds);
            if(isEstablishment){
                // 存在还没有编制完成的版本
                throw new MsgException(CodeEnum.ESTABLISHMENTNUM_ERROR);
            }
        }else {
            // 项目下无签约计划版本
            throw new MsgException(CodeEnum.SIGN_PLAN_VIEW_ERROR);
        }
        // 查询是否有审批中的版本
        checkSubmitToOA(projectId);
        // 根据项目id查询项目名称
        ProjectByStagesDto projectByStagesDto = projectByStagesMapper.selectByOrgdepid(projectId);
        String projectName = projectByStagesDto.getOrgname();
        // 跳转oa审批路径
        String previewUrl = null;
        String flowKey = submitApprovedProjectService.getFlowKey(businessType, projectId);
        if (!StringUtils.isNotBlank(flowKey)) {
            throw new MsgException(CodeEnum.SYS_PROCESS_ID_ERROR);
        }
        String topName = StrUtils.getSubject(versionTypeCode, projectName);

        if (StringUtils.isNotBlank(projectId)) {
            if(null == approvalBill){
                //提交到OA审批时保存业务相关数据
                 approvalBill = addBusinessData(param,flowKey,user,projectName,topName);
            }

            //审批分支变量 签约差异值
            String contractDifference = "0";
            BigDecimal contractDifferenceAmount = BigDecimal.ZERO;
            //签约计划接销管数据后是否手动调整
            String isModified = "0";
            if(versionTypeCode.equals(VersionTypeEunm.DYNAMIC.getKey())){
                //年度目标是否变动数据
                Map<String,Object> map = contractDifference(overallVersionIds,yearMonth,approvalBill);
                contractDifference = (String)map.get("contractDifference");
                contractDifferenceAmount = (BigDecimal)map.get("contractDifferenceAmount");
                isModified = getIsManualSetting(organizationIds);
            }

            // 初次提交为空
            String resInstId = approvalBill.getInstanceId();
            businessKey = approvalBill.getId();
            String state = approvalBill.getState();
            // 任务id
            String taskId = approvalBill.getTaskId();
            // 返回上次的流程key
            String key = approvalBill.getFlowKey();
            // 更新审批说明和流程key
            if (StringUtils.isNotBlank(resInstId) || !(key.equals(flowKey))) {
                // 根据businessKey更新审批说明
                submitApprovedProjectService.updateDescByBusinessKey(businessKey, description, flowKey,user.getAlias(),user.getUsercn());
            }

            JSONObject jsonObjectSave = new JSONObject();
            // 获取oa的token字符串信息
            String authToken = approvalBillService.getAuthToken();
            // 创建流程变量
            JSONObject vars = new JSONObject();
            vars.put("projectOrgCode", projectId);
            vars.put("contractDifference", contractDifference);
            vars.put("contractDifferenceAmount", contractDifferenceAmount);
            vars.put("versionTypeCode", versionTypeCode);
            //是否修改过 0否 1是 后期改动
            vars.put("isModified", isModified);
            // 设置OA保存流程实例接口的参数
            jsonObjectSave.put("account", user.getAlias());
            jsonObjectSave.put("postCode", "");
            jsonObjectSave.put("orgCode", "");
            jsonObjectSave.put("businessKey", businessKey);
            jsonObjectSave.put("flowKey", flowKey);
            jsonObjectSave.put("sysCode", oaSysCode);
            jsonObjectSave.put("subject", topName);
            jsonObjectSave.put("backNormal", false);
            jsonObjectSave.put("agentAccount", "");
            jsonObjectSave.put("vars", vars);
            if (StringUtils.isNotBlank(resInstId)) {
                jsonObjectSave.put("instanceId", resInstId);
            } else {
                jsonObjectSave.put("instanceId", "");
            }
            //发起推演组织编码（流程按此编码 组织为起始，推演审批流程）,设置为当前审批项目id
            jsonObjectSave.put("startOrgCode", projectId);
            String requestSave = jsonObjectSave.toString();
            // 请求OA保存流程实例接口
            JSONObject parseObject = new JSONObject();

            String res = approvalBillService.saveFlow(authToken, requestSave);
            //添加日志
            SysLogBO bo = new SysLogBO();
            bo.setEvent(LogEventEnum.SUBMISSION);
            bo.setSource(LogSrcEnum.PC);
            String hostAddr =WebUtils.getLocalIP();
            String hostName = WebUtils.getHostName();
            bo.setIp(hostAddr);
            bo.setModule("提交签约审批");
            bo.setTitle("保存流程实例");
            bo.setContent(res+" 本地机器名:"+hostName);
         	bo.setType(LogTypeEnum.ZERO.getKey());
         	bo.setIsSuccess(IsSuccessEnum.SUCCESS.getKey());
            sysLogService.add(bo,user.getAlias());

            parseObject = JSONObject.parseObject(res);
            if (!parseObject.getBoolean("state")) {
                CodeEnum.SAVE_FLOW.setMessage(parseObject.getString("message"));
                throw new MsgException(CodeEnum.SAVE_FLOW);
            }

            JSONObject instObject = new JSONObject();
            String instId = parseObject.getString("instId");

            if (StringUtils.isNotBlank(resInstId)){
            	// 撤回发起或者驳回发起
                if (state.equals(ApprovedStateCodeEnum.RECALL.getKey()) || state.equals(ApprovedStateCodeEnum.REJECT.getKey())){
                    instObject.put("taskId",taskId);
                    submitApprovedProjectService.updateApprovalState(businessKey,state,instId,taskId, LocalDateTime.now());
                    saveLog(LogEventEnum.PRESERVATION,"签约计划","修改审判单状态",businessKey+";"+state+";"+instId+";"+taskId+";"+LocalDateTime.now());
                }else {
                	submitApprovedProjectService.updateApprovalState(businessKey, ApprovedStateCodeEnum.DRAFT.getKey(),instId,null, LocalDateTime.now());
                    saveLog(LogEventEnum.PRESERVATION,"签约计划","修改审判单状态",businessKey+";"+ApprovedStateCodeEnum.DRAFT.getKey()+";"+instId+";"+null+";"+LocalDateTime.now());
                }
                instObject.put("proInstId",resInstId);
            }else {
            	 // 修改为草稿
                submitApprovedProjectService.updateApprovalState(businessKey, ApprovedStateCodeEnum.DRAFT.getKey(),instId,null, LocalDateTime.now());
                saveLog(LogEventEnum.PRESERVATION,"签约计划","修改审判单状态",businessKey+";"+ApprovedStateCodeEnum.DRAFT.getKey()+";"+instId+";"+null+";"+LocalDateTime.now());
                instObject.put("proInstId",instId);
            }

            // 修改上传文件信息
            if (CollectionUtils.isNotEmpty(files)) {
                fileUploadService.updateBillId(businessKey, files);
            }

            try {
                String encode = Base64.getEncoder().encodeToString(instObject.toString().getBytes("utf-8"));
                previewUrl = url + encode;
            } catch (UnsupportedEncodingException e) {
                log.error("###### errorMsg: ", e);
            }
        }
        return previewUrl;
    }

    private Map contractDifference(List<String> versionIds, String yearMonth,ApprovalBill approvalBill) {
        Map map = new HashMap();

        //签约差异值
        String contractDifference= "1";
        // 差异金额
        BigDecimal amount = BigDecimal.ZERO;
        //上个审批单年度签约目标金额
        BigDecimal lastYearSignTargetAmount = BigDecimal.ZERO;
        // 调整后年度签约目标金额
        BigDecimal yearSigningTargetAmount = getYearSignTarget(versionIds,yearMonth,1);

        //上个审批单
        ApprovalBill lastResApprovalBill = approvalBillMapper.getLastApprovalBill(approvalBill);
        if(null != lastResApprovalBill){
            //上个审批单提交审批时间
            String lastYearMonth = LocalDateTimeUtils.getLocalMonth(lastResApprovalBill.getCreatedTime());
            //上个审批单全盘签约id
            List<String> lastOverallVersionIds = signPlanApprovedMapper.selectByVersionId(lastResApprovalBill.getId());
            lastYearSignTargetAmount = getYearSignTarget(lastOverallVersionIds,lastYearMonth,1);
            //差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
            amount = yearSigningTargetAmount.subtract(lastYearSignTargetAmount);
            BigDecimal wan = BigDecimal.valueOf(10000);
            //差值小于1万元,全盘年度指标视为无差异
            if(amount.abs().compareTo(wan) <= 0){
                contractDifference = "0";
            }
            map.put("contractDifference",contractDifference);
            map.put("contractDifferenceAmount",amount);
            return map;
        }else {
            map.put("contractDifference",contractDifference);
            map.put("contractDifferenceAmount",yearSigningTargetAmount);
            return map;
        }
    }

    /**
     * 动态版签约计划接销管数据后是否手动调整
     * @param versionIds 版本ids
     * @return
     */
    private String getIsManualSetting(List<String> versionIds) {
        String dynamic = VersionTypeEunm.DYNAMIC.getKey();

        //对动态版本做对比
        for(String versionId :versionIds){
            SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
            if(dynamic.equals(signPlanVersion.getVersionType())){
                //true:无差异,false:有差异
                boolean b = signPlanVersionDetailCopyService.dataCompare(versionId);
                if(!b){
                    return "1";
                }
            }
        }
        return "0";
    }

    /**
     * 查询是否有审批中的版本
     */
    public void checkSubmitToOA(String projectId) {
        int num = signPlanVersionMapper.checkSubmitToOA(projectId, IsDeleteEnum.NO.getKey(),
                VersionStatusEnum.APPROVED.getKey());
        if (num > 0) {
            throw new MsgException(CodeEnum.APPROVED_EXISTENT);
        }
    }

    /**
     * 提交到OA审批时保存业务相关数据
     * @param param
     * @param flowKey
     * @param user
     * @param projectName
     * @param subject
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public ApprovalBill addBusinessData(SignPlanOaParam param, String flowKey,IdmUser user,String projectName, String subject) {
        String businessType = param.getBusinessType();
        String description = param.getDescription();
        //全盘签约计划id集合
        List<String> overallVersionIds = param.getOverallVersionIds();
        //对比差异版本id集合
        List<SignPlanEdition> pkVersionIds = param.getPkVersionIds();
        //项目id
        String projectId = param.getProjectId();
        //本次编制的版本id集合
        List<String> organizationIds = param.getOrganizationIds();
        //编制的年月
        String yearMonth = param.getYearMonth();


        String alias = user.getAlias();
        String name = user.getUsercn();
        String orgCode = user.getDepartmentnumber();
        String postCode = user.getPositionid();

        // 查询签约计划的项目是否存在 草稿 驳回发起 撤回发起的信息
        ApprovalBill resApprovalBill = approvalBillService.getNotSubmitApprovalBill(projectId, businessType);

        String id = null;
        if (null != resApprovalBill) {
            id = resApprovalBill.getId();
        } else {
        	// 查询项目下所有编制中版本
            List<String> versionStatusList = new ArrayList<>();
            versionStatusList.add(VersionStatusEnum.ORGANIZATION.getKey());
            // 查询编制版本是编制中的
            List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper
                    .selectVersionIdsStateList(organizationIds,
                            IsDeleteEnum.NO.getKey(), versionStatusList);
            
            //无审批签约版本，不能提交审批
            if(CollectionUtils.isEmpty(signPlanVersionList)) {
            	throw new MsgException(CodeEnum.NOT_EXIST_APPROVED_VERSION);
            }
        	
            resApprovalBill = new ApprovalBill();
            id = UUIDUtils.create();
            resApprovalBill.setId(id);
            resApprovalBill.setProjectId(projectId);
            resApprovalBill.setBusinessType(businessType);
            resApprovalBill.setFlowKey(flowKey);
            resApprovalBill.setUsercn(name);
            resApprovalBill.setAccount(alias);
            resApprovalBill.setPostCode(postCode);
            resApprovalBill.setOrgCode(orgCode);
            resApprovalBill.setSubject(subject);
            resApprovalBill.setDescription(description);
            resApprovalBill.setCreatedTime(LocalDateTime.now());
            resApprovalBill.setPreviewDate(yearMonth);
            // 未发起
            resApprovalBill.setState(ApprovedStateCodeEnum.NOT_LAUNCHED.getKey());
            approvalBillMapper.saveBusinessData(resApprovalBill);

            saveLog(LogEventEnum.ADD,"签约计划","新增签约审批单","审批单id:"+id);
            
            List<ApprovalBillDetail> list = new ArrayList<>();
            if (null != signPlanVersionList && signPlanVersionList.size() > 0) {
                for (SignPlanVersion sign : signPlanVersionList) {
                    String detailId = UUIDUtils.create();
                    ApprovalBillDetail approvalBillDetail = new ApprovalBillDetail();
                    approvalBillDetail.setId(detailId);
                    approvalBillDetail.setBillId(id);
                    approvalBillDetail.setStageName(sign.getStageName());
                    approvalBillDetail.setVersionType(sign.getVersionType());
                    approvalBillDetail.setVersionId(sign.getId());
                    approvalBillDetail.setVersionNum(sign.getVersionNum());
                    list.add(approvalBillDetail);

                    //变更前的状态
                    String beforeState = sign.getVersionState();
                    sign.setVersionState(VersionStatusEnum.EXAMINEDAPPROVED.getKey());
                    sign.setDescription(description);
                    sign.setUpdateTime(LocalDateTime.now());
                    // 签约计划 状态变为待提交审批
                    signPlanVersionMapper.updateById(sign);
                    saveVersionLog(sign.getId(),VersionEventEnum.INITIATE_APPROVAL.getKey(),beforeState,alias,"发起审批","修改签约版本状态为待提交审批");
                }
                // 添加审批详情表
                approvalBillDetailMapper.insertList(list);
                saveLog(LogEventEnum.PRESERVATION,"签约计划","审批详情表新增数据",list.toString());
                // 添加全盘签约表
                if (CollectionUtils.isNotEmpty(overallVersionIds)) {
                    insertApproved(overallVersionIds, resApprovalBill.getId());
                    saveLog(LogEventEnum.PRESERVATION,"签约计划","全盘签约表新增数据",list.toString());
                }
                // 添加PK签约表
                if (CollectionUtils.isNotEmpty(pkVersionIds)) {
                    insertPk(pkVersionIds, resApprovalBill.getId());
                    saveLog(LogEventEnum.PRESERVATION,"签约计划","PK签约表新增数据",list.toString());
                }
            }
        }
        return resApprovalBill;
    }

    /**
     * 接收销管系统推送的签约计划数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<Boolean,String> insertDynamicSignPlanVersion(String projectFid) {
        HashMap map = new HashMap();
        String localYearMonth = LocalDateTimeUtils.getLocalMonth(LocalDateTime.now());
        //查询指定编制时间最新销售系统签约计划数据
        List<MonthTradePlanDto> monthTradePlanDtoList = monthTradePlanMapper.selectByTimeAndStageId(localYearMonth,
                projectFid);
        if (monthTradePlanDtoList.size() == 0) {
            map.put(false,"供销存系统没有接收到销售管理系统推送的当月签约计划");
            return map;
        }
        // 1、查询该分期最新的动态版签约计划
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectNewesSignVersionByVersionType(projectFid,
                VersionTypeEunm.DYNAMIC.getKey(), IsDeleteEnum.NO.getKey());
        if (null == signPlanVersion) {
            map.put(false,"分期下不存在月度动态版签约计划");
            return map;
        }
        // 审批中、审批成功不进行倒挤
        String versionState = signPlanVersion.getVersionState();
        if (versionState.equals(VersionStatusEnum.APPROVED.getKey()) || versionState.equals(VersionStatusEnum.SUCCESSAPPROVED.getKey())) {
            map.put(false,"月度动态版签约计划在审批中，或已审批通过,不引用销管系统签约计划数据");
            return map;
        }
        String versionId = signPlanVersion.getId();

        // 获取倒挤月份
        String alterYearMonth = getSqueezeYearMonth(LocalDateTime.now());

        // 2、复制销管系统签约数据
        for (MonthTradePlanDto mtpd : monthTradePlanDtoList) {
            SignProductCodeBO signProductCodeBO = new SignProductCodeBO();
            String groupId = mtpd.getGroupId();
            String product = mtpd.getProductCode();
            String productCode = StrUtils.getProductCode(product, 0);
            String businessType = StrUtils.getProductCode(product, 2);
            String freeType = StrUtils.getProductCode(product, 1);
            String proType = StrUtils.getProductCode(product, 3);

            signProductCodeBO.setVersionId(versionId);
            signProductCodeBO.setPid(groupId);
            signProductCodeBO.setProductCode(productCode);
            signProductCodeBO.setBusinessType(businessType);
            signProductCodeBO.setFreeType(freeType);
            signProductCodeBO.setProType(proType);
            signProductCodeBO.setTime(localYearMonth);
            signProductCodeBO.setIsDel(IsDeleteEnum.NO.getKey());
            SignPlanVersionDetail spvd = signPlanVersionDetailMapper.selectBySignPlanTime(signProductCodeBO);
            if (null == spvd) {
                continue;
            }
            int isParking = spvd.getIsParking();
            // <月初存货-本月签约>计划金额
            BigDecimal earlySignSumPrice  = spvd.getEarlySignSumPrice();
            // <本月新供-本月签约>计划金额
            BigDecimal supplySignSumPrice = spvd.getSupplySignSumPrice();

            // 销管本月库存签约金额
            BigDecimal inventoryPrice = mtpd.getInventoryTradeAmountPlan();
            // 销管本月新供签约金额
            BigDecimal newPrice = mtpd.getNewTradeAmountPlan();

            // 月初存货差异金额
            BigDecimal earlyDifferencePrice = inventoryPrice.subtract(earlySignSumPrice);
            // 本月新供差异金额
            BigDecimal supplyDifferencePrice = newPrice.subtract(supplySignSumPrice);
            // 总差异
            BigDecimal sumAmount= earlyDifferencePrice.add(supplyDifferencePrice);


            saveLog(LogEventEnum.EDIT,"签约计划","接销管数据倒挤",mtpd.toString());

            /**
             * 倒挤规则 如果存在签约差异 当月在1-5月，倒挤到6月 当月在6-11月，倒挤到11月 当月在12月，倒挤到下年1月
             */
            if (earlyDifferencePrice.compareTo(BigDecimal.ZERO) != 0  || supplyDifferencePrice.compareTo(BigDecimal.ZERO) != 0) {
                SignPlanVersionDetail signPlanVersionDetail = new SignPlanVersionDetail();
                signPlanVersionDetail.setVersionId(versionId);
                signPlanVersionDetail.setGroupId(groupId);
                signPlanVersionDetail.setGroupType(spvd.getGroupType());
                signPlanVersionDetail.setGroupCode(spvd.getGroupCode());
                signPlanVersionDetail.setGroupName(spvd.getGroupName());
                signPlanVersionDetail.setProductType(spvd.getProductType());
                signPlanVersionDetail.setProductCode(spvd.getProductCode());
                signPlanVersionDetail.setProductName(spvd.getProductName());
                signPlanVersionDetail.setProProductCode(spvd.getProProductCode());
                signPlanVersionDetail.setProProductType(spvd.getProProductType());
                signPlanVersionDetail.setBusinessType(spvd.getBusinessType());
                signPlanVersionDetail.setBusinessTypeName(spvd.getBusinessTypeName());
                signPlanVersionDetail.setFreeType(spvd.getFreeType());
                signPlanVersionDetail.setFreeTypeName(spvd.getFreeTypeName());
                signPlanVersionDetail.setProType(spvd.getProType());
                signPlanVersionDetail.setProTypeName(spvd.getProTypeName());
                signPlanVersionDetail.setEarlySignRoomCount(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlySignRoomArea(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlySignAvgPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlySignSumPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySignRoomCount(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySignRoomArea(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySignAvgPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySignSumPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlySignRoomCountActual(0);
                signPlanVersionDetail.setEarlySignRoomAreaActual(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlySignAvgPriceActual(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlySignSumPriceActual(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlyRoomCount(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlyCanSignRoomCount(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlyRoomArea(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlyAvgRoomArea(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlyAvgPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setEarlySumPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplyRoomCount(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplyCanSignRoomCount(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplyRoomArea(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplyAvgRoomArea(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplyAvgPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySumPrice(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySignRoomCountActual(0);
                signPlanVersionDetail.setSupplySignRoomAreaActual(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySignAvgPriceActual(BigDecimal.ZERO);
                signPlanVersionDetail.setSupplySignSumPriceActual(BigDecimal.ZERO);
                signPlanVersionDetail.setYearEalySignRoomCountActual(0);
                signPlanVersionDetail.setYearEalySignRoomAreaActual(BigDecimal.ZERO);
                signPlanVersionDetail.setYearEalySignAvgPriceActual(BigDecimal.ZERO);
                signPlanVersionDetail.setYearEalySignSumPriceActual(BigDecimal.ZERO);
                signPlanVersionDetail.setIsParking(isParking);
                signPlanVersionDetail.setIsEstablishment(WhetherEnum.NO.getKey());
                signPlanVersionDetail.setIsDelete(WhetherEnum.NO.getKey());
                signPlanVersionDetail.setCreateBy(Constants.SYS);

                // 1、复制销管系统签约数据，重新计算当月数据
                if (earlyDifferencePrice.compareTo(BigDecimal.ZERO) != 0 ) {
                    // <月初存货-本月签约>计划金额 = 销管本月库存签约金额
                    spvd.setEarlySignSumPrice(inventoryPrice);
                    // <月初存货-本月签约>计划均价
                    if (inventoryPrice.compareTo(BigDecimal.ZERO)==0) {
                        spvd.setEarlySignAvgPrice(BigDecimal.ZERO);
                    }else {
                        spvd.setEarlySignAvgPrice(spvd.getEarlyAvgPrice());
                    }
                    if(isParking == WhetherEnum.YES.getKey()){
                        // <月初存货-本月签约>计划套数
                        spvd.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(spvd.getEarlySignSumPrice(), spvd.getEarlySignAvgPrice(), 5));
                    }else {
                        // <月初存货-本月签约>计划面积
                        spvd.setEarlySignRoomArea(BigDecimalUtils.divBigDecimal(spvd.getEarlySignSumPrice(), spvd.getEarlySignAvgPrice(), 4));
                        // <月初存货-本月签约>计划套数
                        spvd.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(spvd.getEarlySignRoomArea(), spvd.getEarlyAvgRoomArea(), 5));
                    }
                    // <月初存货>剩余可签套数 = <月初存货>套数 - <月初存货-本月签约>套数-计划
                    spvd.setEarlyCanSignRoomCount(spvd.getEarlyRoomCount().subtract(spvd.getEarlySignRoomCount()));
                }
                if (supplyDifferencePrice.compareTo(BigDecimal.ZERO) != 0 ) {
                    // <本月新供-本月签约>计划金额 = 销管本月库存签约金额
                    spvd.setSupplySignSumPrice(newPrice);
                    // <本月新供-本月签约>计划均价
                    if (supplyDifferencePrice.compareTo(BigDecimal.ZERO)==0) {
                        spvd.setSupplySignAvgPrice(BigDecimal.ZERO);
                    }else {
                        spvd.setSupplySignAvgPrice(spvd.getSupplyAvgPrice());
                    }
                    if(isParking == WhetherEnum.YES.getKey()){
                        // <本月新供-本月签约>计划套数
                        spvd.setSupplySignRoomCount(BigDecimalUtils.divBigDecimal(spvd.getSupplySignSumPrice(), spvd.getSupplySignAvgPrice(), 5));
                    }else {
                        // <本月新供-本月签约>计划面积
                        spvd.setSupplySignRoomArea(BigDecimalUtils.divBigDecimal(spvd.getSupplySignSumPrice(), spvd.getSupplySignAvgPrice(), 4));
                        // <本月新供-本月签约>计划套数
                        spvd.setSupplySignRoomCount(BigDecimalUtils.divBigDecimal(spvd.getSupplySignRoomArea(), spvd.getSupplyAvgRoomArea(), 5));
                    }
                    // <本月新供>剩余可签套数=<本月新供>套数 - <本月新供-本月签约>套数-计划
                    spvd.setSupplyCanSignRoomCount(spvd.getSupplyRoomCount().subtract(spvd.getSupplySignRoomCount()));
                }
                spvd.setIsLock(WhetherEnum.YES.getKey()); // 是否锁定 isLock
                // 保存倒挤月份产品构成签约计划
                signPlanVersionDetailMapper.updateById(spvd);

                saveLog(LogEventEnum.EDIT,"签约计划","接销管数据倒挤",spvd.toString());


                // 2、修改当月至倒挤月份月初存货供货数据
                // 记录月初存货
                SignPlanVersionDetailBO spvdBO = new SignPlanVersionDetailBO();
                spvdBO.initialize();

                // 倒挤月份
                LocalDateTime latestSupplyDate = LocalDateTimeUtils.toLocalDateTime(String.valueOf(alterYearMonth));
                // 按月份初始化
                for (LocalDateTime time = spvd.getSignPlanTime(); LocalDateTimeUtils.compareDate(time, latestSupplyDate,
                        1); time = LocalDateTimeUtils.addDate(time)) {

                    saveLog(LogEventEnum.EDIT,"签约计划","接销管数据倒挤",time.toString() + "/"+spvdBO.toString());

                    signProductCodeBO.setTime(LocalDateTimeUtils.getLocalMonth(time));
                    SignPlanVersionDetail newspvd = signPlanVersionDetailMapper.selectBySignPlanTime(signProductCodeBO);
                    // 是否当前月
                    if (!LocalDateTimeUtils.compareDate(time, LocalDateTime.now(), 2)) {
                        if (null == newspvd) {
                            newspvd = new SignPlanVersionDetail();
                            try {
                                newspvd = signPlanVersionDetail.clone();
                            } catch (CloneNotSupportedException e) {
                                log.error("###### errorMsg: ", e);
                            }
                        }

                        newspvd.setSignPlanTime(time); // 签约计划时间(xxxx-xx-01)
                        newspvd.setIsLock(WhetherEnum.NO.getKey()); // 是否锁定 isLock
                        newspvd.setCreateTime(LocalDateTime.now()); // 创建时间
                        newspvd.setUpdateBy(Constants.SYS); // 更新人员
                        newspvd.setUpdateTime(LocalDateTime.now()); // 更新时间

                        // <月初存货>套数
                        newspvd.setEarlyRoomCount(spvdBO.getEarlyRoomCount());
                        // <月初存货>面积
                        newspvd.setEarlyRoomArea(spvdBO.getEarlyRoomArea());
                        // <月初存货>套均面积
                        newspvd.setEarlyAvgRoomArea(spvdBO.getEarlyAvgRoomArea());
                        // <月初存货>均价
                        newspvd.setEarlyAvgPrice(spvdBO.getEarlyAvgPrice());
                        // <月初存货>金额
                        newspvd.setEarlySumPrice(spvdBO.getEarlySumPrice());

                        // 是否倒挤月份
                        if (LocalDateTimeUtils.compareDate(time, latestSupplyDate, 2)) {
                            // <月初存货-本月签约>计划金额= <月初存货-本月签约>计划金额- 月初存货差异金额
                            newspvd.setEarlySignSumPrice(newspvd.getEarlySignSumPrice().subtract(sumAmount));
                            if (newspvd.getEarlySignSumPrice().compareTo(BigDecimal.ZERO) == 0 ) {
                                // <月初存货-本月签约>计划均价=0
                                newspvd.setEarlySignAvgPrice(BigDecimal.ZERO);
                            } else {
                                // <月初存货-本月签约>计划均价=<月初存货>均价
                                newspvd.setEarlySignAvgPrice(newspvd.getEarlyAvgPrice());
                            }
                            if (isParking == WhetherEnum.YES.getKey()) {
                                // <月初存货-本月签约>计划套数 = <月初存货-本月签约>计划金额 / <月初存货-本月签约>计划均价
                                newspvd.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(
                                        newspvd.getEarlySignSumPrice(), newspvd.getEarlySignAvgPrice(), 5));
                            } else {
                                // <月初存货-本月签约>计划面积=<月初存货-本月签约>计划金额 / <月初存货>计划均价
                                newspvd.setEarlySignRoomArea(BigDecimalUtils.divBigDecimal(
                                        newspvd.getEarlySignSumPrice(), newspvd.getEarlySignAvgPrice(), 4));
                                // <月初存货-本月签约>计划套数 = <月初存货-本月签约>计划面积 / <月初存货-本月签约>套均面积
                                newspvd.setEarlySignRoomCount(BigDecimalUtils.divBigDecimal(
                                        newspvd.getEarlySignRoomArea(), newspvd.getEarlyAvgRoomArea(), 5));
                            }
                        }
                    }

                    // <月初存货>剩余可签套数=<月初存货>套数 - <月初存货-本月签约>套数-计划
                    newspvd.setEarlyCanSignRoomCount(newspvd.getEarlyRoomCount().subtract(newspvd.getEarlySignRoomCount()));
                    // <本月新供>剩余可签套数=<本月新供>套数 - <本月新供-本月签约>套数-计划
                    newspvd.setSupplyCanSignRoomCount(newspvd.getSupplyRoomCount().subtract(newspvd.getSupplySignRoomCount()));

                    /* 计算下个月月初存货 */
                    // 下个月初存货的套数 = <月初存货>剩余可签套数+<本月新供标题>剩余可签套数
                    spvdBO.setEarlyRoomCount(newspvd.getEarlyCanSignRoomCount().add(newspvd.getSupplyCanSignRoomCount()));
                    // 下个月初存货的金额 = <月初存货>金额 + <本月新供>金额 - <月初存货-本月签约>金额-计划 - <本月新供-本月签约>金额-计划
                    spvdBO.setEarlySumPrice(newspvd.getEarlySumPrice().add(newspvd.getSupplySumPrice())
                            .subtract(newspvd.getEarlySignSumPrice()).subtract(newspvd.getSupplySignSumPrice()));
                    if (isParking == WhetherEnum.YES.getKey()) {
                        // 下个月初存货的均价 = 下个月初存货的金额 / 下个月初存货的套数
                        spvdBO.setEarlyAvgPrice(BigDecimalUtils
                                .divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomCount(), 4));
                    } else {
                        // 下个月初存货的面积=<月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>面积-计划 - <本月新供-本月签约>面积-计划
                        spvdBO.setEarlyRoomArea(newspvd.getEarlyRoomArea().add(newspvd.getSupplyRoomArea())
                                .subtract(newspvd.getEarlySignRoomArea()).subtract(newspvd.getSupplySignRoomArea()));
                        // 下个月初存货的套均面积 = 下个月初存货的面积/下个月初存货的套数
                        spvdBO.setEarlyAvgRoomArea(BigDecimalUtils
                                .divBigDecimal(spvdBO.getEarlyRoomArea(), spvdBO.getEarlyRoomCount(), 4));
                        // 下个月初存货的均价=下个月初存货的金额 / 下个月初存货的面积
                        spvdBO.setEarlyAvgPrice(
                                BigDecimalUtils.divBigDecimal(spvdBO.getEarlySumPrice(), spvdBO.getEarlyRoomArea(), 4));
                    }

                    // 存在则修改,不存在则新增
                    if (newspvd.getId() != null) {
                        newspvd.setUpdateTime(LocalDateTime.now());
                        newspvd.setIsEstablishment(WhetherEnum.NO.getKey());
                        signPlanVersionDetailMapper.updateById(newspvd);
                    } else {
                        newspvd.setId(UUIDUtils.create());// 主键
                        newspvd.setSignPlanTime(time);
                        newspvd.setIsLock(WhetherEnum.NO.getKey());
                        newspvd.setCreateTime(LocalDateTime.now());
                        signPlanVersionDetailMapper.insert(newspvd);
                    }
                    saveLog(LogEventEnum.PRESERVATION,"签约计划","接销管数据倒挤",newspvd.toString());
                }
            } else {
                spvd.setIsLock(WhetherEnum.YES.getKey());
                signPlanVersionDetailMapper.updateById(spvd);
            }
        }

        //对新建的签约计划详情做快照
        signPlanVersionDetailCopyService.copyDetail(versionId);
        map.put(true,"调用成功");

        saveLog(LogEventEnum.EDIT,"签约计划", "接销管数据倒挤", map.toString());
        return map;
    }

    /**
     * 获取签约倒挤年月
     * @return
     */
    private String getSqueezeYearMonth(LocalDateTime time) {
        // 倒挤月份
        String squeezeYearMonth = null;

        if (LocalDateTimeUtils.isBetweenTwoMonth(1, 5, time.getMonthValue())) {
            squeezeYearMonth = LocalDateTimeUtils.toStrYearMonth(time.getYear(), 6);
        } else if (LocalDateTimeUtils.isBetweenTwoMonth(6, 11, time.getMonthValue())) {
            squeezeYearMonth = LocalDateTimeUtils.toStrYearMonth(time.getYear(), 12);
        } else if (LocalDateTimeUtils.isBetweenTwoMonth(12, 12, time.getMonthValue())) {
            squeezeYearMonth = LocalDateTimeUtils.toStrYearMonth(time.getYear() + 1, 1);
        }
        return squeezeYearMonth;
    }

    /**
     * 检查是否存在项目数据
     */
    @Override
    public int checkApprovedVersionIsNull(String projectId) {
        return signPlanVersionMapper.checkApprovedVersionIsNull(projectId, IsDeleteEnum.NO.getKey());
    }

    /**
     * 根据projectId查询需要审批的项目的分期 是否存在编制中，待提交审批，已驳回的版本
     */
    @Override
    public List<SignPlanVersion> selectApprovedProject(String projectId) {
        List<String> str = new ArrayList<>();
        str.add(VersionStatusEnum.ORGANIZATION.getKey());
        str.add(VersionStatusEnum.EXAMINEDAPPROVED.getKey());
        str.add(VersionStatusEnum.FAILUREAPPROVED.getKey());
        return signPlanVersionMapper.selectApprovedProject(projectId, IsDeleteEnum.NO.getKey(),str);
    }

    /**
     * 查询是否存在编制中或者审批中的版本
     */
    @Override
    public int checkApprovedVersion(String projectId) {
        List<String> str = new ArrayList<>();
        str.add(VersionStatusEnum.NOTPREPARED.getKey());
        str.add(VersionStatusEnum.APPROVED.getKey());
        return signPlanVersionMapper.checkVersion(projectId, IsDeleteEnum.NO.getKey(), str);
    }


    // 组团排序
    private void groupOrderBy(List<SignPlanVersionDetailDto> dtoList,String type) {
        if(OrgTypeEunm.ORG_GROUP.getKey().equals(type)){
            dtoList.sort((a, b) -> {
                String aName = a.getName();
                String bName = b.getName();

                String[] aStr = aName.split("组团");
                String[] bStr = bName.split("组团");

                if (aStr[0].equals("车位")) {
                    aStr[0] = "10000000";
                }
                if (bStr[0].equals("车位")) {
                    bStr[0] = "10000000";
                }

                String aValue = NumberEnum.getKeyByValue(aStr[0]);
                String bValue = NumberEnum.getKeyByValue(bStr[0]);

                return aValue == null ? 1 : bValue == null ? -1 : Integer.valueOf(aValue) - Integer.valueOf(bValue);
            });
        }else if(OrgTypeEunm.ORG_STAGE.getKey().equals(type)){
            //分期排序
            dtoList.sort((a,b) ->{
                String[] aStr = a.getName().split("期");
                String[] bStr = b.getName().split("期");

                String aValue = NumberEnum.getKeyByValue(aStr[0]);
                String bValue = NumberEnum.getKeyByValue(bStr[0]);

                return aValue == null ? 1 : bValue == null ? -1 : Integer.valueOf(aValue) - Integer.valueOf(bValue);
            });
        }
    }



    /**
     * 签约计划审批通过后的 添加版本Id到审批记录信息中
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insertApproved(List<String> successIds, String versionId) {
        // 插入审批通过的版本id
        if (successIds.size() > 0) {
            for (String string : successIds) {
                SignPlanApproved se = new SignPlanApproved();
                se.setCreateTime(new Date());
                se.setSignPlanId(versionId);
                se.setVersionId(string);
                se.setId(UUIDUtils.create());
                signPlanApprovedMapper.insert(se);
            }
        }
    }

    /**
     * 查询全盘项目下的所有分期最新的签约计划数据(除了待编制状态)
     *
     * @param projectId
     */
    public List<String> getOverallList(String projectId) {
        List<String> versionStates = new ArrayList<>();
        versionStates.add(VersionStatusEnum.ORGANIZATION.getKey());
        versionStates.add(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        versionStates.add(VersionStatusEnum.EXAMINEDAPPROVED.getKey());
        versionStates.add(VersionStatusEnum.APPROVED.getKey());
        versionStates.add(VersionStatusEnum.FAILUREAPPROVED.getKey());
        List<String> list = new ArrayList<>();
        //查询项目下的所有分期id
        List<String> stageIdList = projectByStagesMapper.selectByParentOrgId(projectId);
        if (CollectionUtils.isNotEmpty(stageIdList)) {
            for (String stageId : stageIdList) {
                MdmStage mdmStage = stageService.selectById(stageId);
                if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
                    //剔除禁用分期
                    continue;
                }
               if(!getStageIsSaly(stageId)){
                   //剔除全盘最新供货版本未全部可租或不可租售的分期
                   continue;
               }
               SignPlanVersion version = signPlanVersionMapper.selectLastByprojectfIdAndVersionStates(stageId, versionStates,IsDeleteEnum.NO.getKey());
               if(null ==version){
                   continue;
               }
               //不存在在途版本点的分期,取全盘最新的版本
               if(VersionStatusEnum.SUCCESSAPPROVED.getKey().equals(version.getVersionState())){
                   version = getOverallByProjectf(stageId);
                   if(null ==version){
                       continue;
                   }else {
                       list.add(version.getId());
                   }
               }else {
                   list.add(version.getId());
               }
            }
        }
        return list;
    }

    /**
     * 审批中和审批预览的本次编制版本
     *
     * @param organizationId 本次编制签约计划版本id
     */
    public OrganizationDto getEditonNames(List<String> organizationId, MdmProject mdm) {
        OrganizationDto dto = new OrganizationDto();
        List<String> stageNames = new ArrayList<>();
        String topName = "";
        //是否预算版 0 是  1 不是
        int flag = 0;
        //审批优先级  动态版 > 战规版 > 投资版 > 预算版
        String versionTypeCode = "";
        List<String> versionNames = new ArrayList<>();

        if (organizationId.size() > 0) {
            List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.
                    selectByVersionIds(organizationId, IsDeleteEnum.NO.getKey());
            for (SignPlanVersion sp : signPlanVersionList) {
                String stageName = sp.getStageName();
                String versionName = StrUtils.getVersionName(sp.getVersionType());
                // 组合签约计划全路径名称
                String fullName = stageName + ": " + VersionUtils.getFullName(sp);

                stageNames.add(fullName);
                if (!sp.getVersionType().equals(VersionTypeEunm.BUDGET.getKey())) {
                    flag = 1;
                }
                versionNames.add(versionName);
            }
        }
        //审批优先级  动态版 > 战规版 > 投资版 > 预算版
        if(versionNames.contains(VersionTypeEunm.DYNAMIC.getValue())){
            versionTypeCode = VersionTypeEunm.DYNAMIC.getKey();
        }else if(versionNames.contains(VersionTypeEunm.WARPLAN.getValue())){
            versionTypeCode = VersionTypeEunm.WARPLAN.getKey();
        }else if(versionNames.contains(VersionTypeEunm.INVESTMENT.getValue())){
            versionTypeCode = VersionTypeEunm.INVESTMENT.getKey();
        }else if(versionNames.contains(VersionTypeEunm.BUDGET.getValue())){
            versionTypeCode = VersionTypeEunm.BUDGET.getKey();
        }
        int type = StrUtils.getPkType(versionNames);
        topName = StrUtils.getSubject(versionTypeCode, mdm.getProjectName());

        //分期排序
        stageNames.sort((a,b) ->{
            String[] aStr = a.split("期");
            String[] bStr = b.split("期");

            String aValue = NumberEnum.getKeyByValue(aStr[0]);
            String bValue = NumberEnum.getKeyByValue(bStr[0]);

            return aValue == null ? 1 : bValue == null ? -1 : Integer.valueOf(aValue) - Integer.valueOf(bValue);
        });
        dto.setFlag(flag);
        dto.setTopName(topName);
        dto.setStageNames(stageNames);
        dto.setVersionTypeCode(versionTypeCode);
        dto.setType(type);
        return dto;
    }

    /**
     * 获取全盘签约id
     */
    public List<String> getOverallVersionIds(String projectId, ApprovalBill approvalBill) {
        if (approvalBill != null) {
            if (StringUtils.isNotBlank(approvalBill.getId())) {
                List<String> overallVersionIds = signPlanApprovedMapper.selectByVersionId(approvalBill.getId());
                if (overallVersionIds.size() == 0) {
                    throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
                }
                return overallVersionIds;
            }
        } else {
            // 调整后全盘签约版本数据
            List<String> overallVersionIds = getOverallList(projectId);
            if (overallVersionIds.size() == 0) {
                throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
            }
            // 全盘版本id
            return overallVersionIds;
        }

        return new ArrayList<>();
    }

    /*
     *
     * 签约计划审批通过后的 添加版本Id到审批记录信息中
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void insertPk(List<SignPlanEdition> signPlanEditions, String versionId) {
        // 插入审批通过的版本id
        if (CollectionUtils.isNotEmpty(signPlanEditions)) {
            for (SignPlanEdition se : signPlanEditions) {
                se.setCreateTime(new Date());
                se.setSignPlanId(versionId);
                se.setId(UUIDUtils.create());
                signPlanEditionMapper.insert(se);
            }
        }
    }

    /**
     * 对比版本差异
     * @param projectId 项目id
     * @param flag 是否预算版（0 是  1 不是）
     * @param yearSigningTargetAmount 年度签约目标金额
     * @param year 年份
     * @param approvalBill 审批单
     * @return
     */
    public List<DifferencesDto> getPkInfo(String projectId,int flag,BigDecimal yearSigningTargetAmount,String yearMonth,ApprovalBill approvalBill) {
        List<DifferencesDto> list = new ArrayList<>();
        List<String> pkVersionIds = new ArrayList<>();
        List<SignPlanEdition> signPlanEditioList = new ArrayList<>();
        List<SignPlanEdition> signPlanEditios = new ArrayList<>();
        BigDecimal vsAmount;
        if (approvalBill != null) {
            //从审批单中获取本次编制的版本
            signPlanEditioList = signPlanEditionMapper.selectByVersionId(approvalBill.getId());
            if(0 == flag){
                //投资版差异
                signPlanEditios = signPlanEditioList.stream().filter(s -> StringUtils.isNotEmpty(s.getType()) && VersionContrastType.VSIBVESTMENT.getKey().equals(s.getType())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSIBVESTMENT.getValue());
                    list.add(dto);
                }
                //战规版差异
                signPlanEditios = signPlanEditioList.stream().filter(s -> StringUtils.isNotEmpty(s.getType()) && s.getType().equals(VersionContrastType.VSSTRATEGY.getKey())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSSTRATEGY.getValue());
                    list.add(dto);
                }
                //上一版预算版差异
                signPlanEditios = signPlanEditioList.stream().filter(s -> StringUtils.isNotEmpty(s.getType()) && s.getType().equals(VersionContrastType.VSPREVIOUSBUDGET.getKey())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSPREVIOUSBUDGET.getValue());
                    list.add(dto);
                }
            }else {
                //预算版版差异
                signPlanEditios = signPlanEditioList.stream().filter(s -> StringUtils.isNotEmpty(s.getType()) && s.getType().equals(VersionContrastType.VSBUDGET.getKey())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSBUDGET.getValue());
                    list.add(dto);
                }
                //调整前差异
                signPlanEditios = signPlanEditioList.stream().filter(s -> StringUtils.isNotEmpty(s.getType()) && s.getType().equals(VersionContrastType.VSBEFOREADJUSTMMENT.getKey())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSBEFOREADJUSTMMENT.getValue());
                    list.add(dto);
                }
            }
        } else {
            if(0 == flag){
                //1、投资版差异
                //获取指定对比类型的版本id列表
                signPlanEditios = getVsVersionIdList(projectId,VersionContrastType.VSIBVESTMENT.getKey());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSIBVESTMENT.getValue());
                    list.add(dto);
                }

                //2、战规版差异
                //获取指定对比类型的版本id列表
                signPlanEditios = getVsVersionIdList(projectId,VersionContrastType.VSSTRATEGY.getKey());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSSTRATEGY.getValue());
                    list.add(dto);
                }
                //3、上一版预算版差异
                //获取指定对比类型的版本id列表
                signPlanEditios = getVsVersionIdList(projectId,VersionContrastType.VSPREVIOUSBUDGET.getKey());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSPREVIOUSBUDGET.getValue());
                    list.add(dto);
                }
            }else {
                //1、预算版版差异
                //获取指定对比类型的版本id列表
                signPlanEditios = getVsVersionIdList(projectId,VersionContrastType.VSBUDGET.getKey());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSBUDGET.getValue());
                    list.add(dto);
                }

                //2、调整前差异
                //获取指定对比类型的版本id列表
                signPlanEditios = getVsVersionIdList(projectId,VersionContrastType.VSBEFOREADJUSTMMENT.getKey());
                if(CollectionUtils.isNotEmpty(signPlanEditios)){
                    DifferencesDto dto = new DifferencesDto();
                    pkVersionIds = signPlanEditios.stream().map(s -> s.getVersionId()).collect(Collectors.toList());
                    //查询版本列表年度签约目标金额
                    vsAmount = getYearSignTarget(pkVersionIds,yearMonth,0);
                    // 差异金额 = 年度签约目标金额 - xxx版的当年年度签约目标金额
                    BigDecimal amount = yearSigningTargetAmount.subtract(vsAmount);
                    // 差异比例 = 差异金额 / xxx版的当年年度签约目标金额
                    String rate = StrUtils.rateAchievement(amount, vsAmount);

                    dto.setPkVersionIds(signPlanEditios);
                    dto.setAmount(amount.toString());
                    dto.setRate(vsAmount.compareTo(BigDecimal.ZERO)== 0 ? "_":rate.replace("%", ""));
                    dto.setName(VersionContrastType.VSBEFOREADJUSTMMENT.getValue());
                    list.add(dto);
                }
            }
        }
        return list;
    }

    /**
     * 获取项目下对应对比类型的版本
     * @param projectId 项目id
     * @param type 对比类型
     * @return
     */
    private List<SignPlanEdition> getVsVersionIdList(String projectId, String type) {
        //查询项目下分期列表
        List<MdmStage> mdmStages = stageService.selectByProjectId(projectId, IsDeleteEnum.NO.getKey());
        List<String> projectfIds = mdmStages.stream().map(s -> s.getProjectFid()).collect(Collectors.toList());

        List<SignPlanEdition> signPlanEditios = new ArrayList<>();
        List<String> versionTypes = new ArrayList<>();
        List<String> versionStates = new ArrayList<>();
        versionStates.add(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        switch (type){
            case "vsInvestment":
                //查询分期下最新审批通过的投资考核版
                for(String projectfId : projectfIds){
                    SignPlanVersion spv = signPlanVersionMapper.selectByIsAssessmentVersion(projectfId,WhetherEnum.YES.getKey(),VersionStatusEnum.SUCCESSAPPROVED.getKey(),IsDeleteEnum.NO.getKey());
                    if(null == spv){
                        signPlanEditios = new ArrayList<>();
                        break;
                    }
                    SignPlanEdition signPlanEdition = new SignPlanEdition();
                    signPlanEdition.setVersionId(spv.getId());
                    signPlanEdition.setType(type);
                    signPlanEditios.add(signPlanEdition);
                }
                break;
            case "vsStrategy":
                //查询分期下最新审批通过的战规版
                versionTypes.add(VersionTypeEunm.WARPLAN.getKey());
                for(String projectfId : projectfIds){
                    SignPlanVersion spv = signPlanVersionMapper.selectNewesVersion(projectfId,versionTypes,versionStates,IsDeleteEnum.NO.getKey());
                    if(null == spv){
                        signPlanEditios = new ArrayList<>();
                        break;
                    }
                    SignPlanEdition signPlanEdition = new SignPlanEdition();
                    signPlanEdition.setVersionId(spv.getId());
                    signPlanEdition.setType(type);
                    signPlanEditios.add(signPlanEdition);
                }
                break;
            case "vsPreviousBudget":
                //查询分期下最新审批通过的预算版
                versionTypes.add(VersionTypeEunm.BUDGET.getKey());
                for(String projectfId : projectfIds){
                    SignPlanVersion spv = signPlanVersionMapper.selectNewesVersion(projectfId,versionTypes,versionStates,IsDeleteEnum.NO.getKey());
                    if(null == spv){
                        signPlanEditios = new ArrayList<>();
                        break;
                    }
                    SignPlanEdition signPlanEdition = new SignPlanEdition();
                    signPlanEdition.setVersionId(spv.getId());
                    signPlanEdition.setType(type);
                    signPlanEditios.add(signPlanEdition);
                }
                break;
            case "vsBudget":
                //查询分期下最新审批通过的预算版
                versionTypes.add(VersionTypeEunm.BUDGET.getKey());
                for(String projectfId : projectfIds){
                    SignPlanVersion spv = signPlanVersionMapper.selectNewesVersion(projectfId,versionTypes,versionStates,IsDeleteEnum.NO.getKey());
                    if(null == spv){
                        signPlanEditios = new ArrayList<>();
                        break;
                    }
                    SignPlanEdition signPlanEdition = new SignPlanEdition();
                    signPlanEdition.setVersionId(spv.getId());
                    signPlanEdition.setType(type);
                    signPlanEditios.add(signPlanEdition);
                }
                break;
            case "vsBeforeAdjustment":
                //查询分期下最新审批通过的版本
                versionTypes.add(VersionTypeEunm.INVESTMENT.getKey());
                versionTypes.add(VersionTypeEunm.WARPLAN.getKey());
                versionTypes.add(VersionTypeEunm.BUDGET.getKey());
                versionTypes.add(VersionTypeEunm.DYNAMIC.getKey());
                for(String projectfId : projectfIds){
                    MdmStage mdmStage = stageService.selectById(projectfId);
                    if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
                        //剔除禁用分期
                        continue;
                    }
                   if(!getStageIsSaly(projectfId)){
                       //剔除全盘最新供货版本未全部可租或不可租售的分期
                       continue;
                   }
                    SignPlanVersion spv = signPlanVersionMapper.selectNewesVersion(projectfId,versionTypes,versionStates,IsDeleteEnum.NO.getKey());
                    if(null == spv){
                        signPlanEditios = new ArrayList<>();
                        break;
                    }
                    SignPlanEdition signPlanEdition = new SignPlanEdition();
                    signPlanEdition.setVersionId(spv.getId());
                    signPlanEdition.setType(type);
                    signPlanEditios.add(signPlanEdition);
                }
                break;
        }
        return signPlanEditios;
    }


    /**
     * 校验项目审批预览
     * @param projectId
     */
    public void checkProjectPreview(String projectId) {
        List<String> str = new ArrayList<>();
        str.add(VersionStatusEnum.ORGANIZATION.getKey());
        str.add(VersionStatusEnum.EXAMINEDAPPROVED.getKey());
        str.add(VersionStatusEnum.APPROVED.getKey());
        str.add(VersionStatusEnum.FAILUREAPPROVED.getKey());
        str.add(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        int count = signPlanVersionMapper.checkVersion(projectId, IsDeleteEnum.NO.getKey(), str);
        if (count == 0) {
            //项目下无签约计划版本,不能提交审批
            throw new MsgException(CodeEnum.SIGN_PLAN_VIEW_ERROR);
        }
    }

    /**
     * 获取本次编制的版本id
     *
     * @param projectId
     * @return
     */
    private List<String> getOrganizationIds(String projectId, ApprovalBill approvalBill) {
        if (approvalBill != null) {
            if (StringUtils.isNotBlank(approvalBill.getId())) {
                //从审批单中获取本次编制的版本
                List<String> versionIds = approvalBillDetailMapper.selectVersionIds(approvalBill.getId());
                if (null == versionIds || versionIds.isEmpty()) {
                    throw new MsgException(CodeEnum.SIGN_PLAN_ORGANIZATION_ERROR);
                }
                return versionIds;
            }
        } else {
            List<String> statusList = new ArrayList<>();
            statusList.add(VersionStatusEnum.ORGANIZATION.getKey());
            statusList.add(VersionStatusEnum.FAILUREAPPROVED.getKey());

            //新创建的是编制中的版本id
            List<String> str = new ArrayList<>();
            str.add(VersionStatusEnum.ORGANIZATION.getKey());
            str.add(VersionStatusEnum.FAILUREAPPROVED.getKey());
            List<String> versionIds = signPlanVersionMapper.selectOrganizationIds(projectId, IsDeleteEnum.NO.getKey(), statusList);
            if (CollectionUtils.isEmpty(versionIds)) {
                throw new MsgException(CodeEnum.SIGN_PLAN_ORGANIZATION_ERROR);
            }

            //版本数据列表
            List<SignPlanVersion> signPlanVersions = signPlanVersionMapper.selectByVersionIds(versionIds, WhetherEnum.NO.getKey());
            for (SignPlanVersion signPlanVersion : signPlanVersions) {
                MdmStage mdmStage = stageService.selectById(signPlanVersion.getProjectfId());
                if (StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())) {
                    throw new MsgException(CodeEnum.APPROVED_INACTIVE_STAGE_ERROR);
                }
                if (!getStageIsSaly(signPlanVersion.getProjectfId())) {
                    throw new MsgException(CodeEnum.APPROVED_NOT_BUILD_SALY_TYPE_ERROR);
                }
            }
            return versionIds;
        }
        throw new MsgException(CodeEnum.SIGN_PLAN_ORGANIZATION_ERROR);
    }

    /**
     * 查询是否可以提交审批
     *
     * @param projectId
     */
    private void checkSubmitApproval(String projectId) {
        List<String> str = new ArrayList<>();
        str.add(VersionStatusEnum.ORGANIZATION.getKey());
        str.add(VersionStatusEnum.EXAMINEDAPPROVED.getKey());
        str.add(VersionStatusEnum.FAILUREAPPROVED.getKey());
        int num = signPlanVersionMapper.checkVersion(projectId, IsDeleteEnum.NO.getKey(), str);
        if (num > 0) {
            // 可以提交审批
        } else {
            //没有待提交审批或编制中或驳回的版本，不能提交审批
            throw new MsgException(CodeEnum.APPROVED_NON_EXISTENT);
        }
    }

    /**
     * 项目最早签约计划年份大于当年时，签约计划审批按照最早签约计划年份计算年度数据；否则按照当年计算年度数据
     * 返回yyyy-mm
     * @param versionIds
     */
    private String getEarliestDate(List<String> versionIds) {
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        //获取最早签约时间
        LocalDateTime earliestDate = signPlanVersionDetailMapper.selectEarliestDate(versionIds, IsDeleteEnum.NO.getKey());
        if (null != earliestDate) {
            if (earliestDate.isAfter(now)) {
                //大于当前时间
                return LocalDateTimeUtils.getLocalMonth(earliestDate);
            } else {
                //小于当前时间
                return LocalDateTimeUtils.getLocalMonth(now);
            }
        } else {
            //签约时间不存在
            return LocalDateTimeUtils.getLocalMonth(now);
        }
    }


    /**
     * 获取版本的产品构成年度新供金额
     * @param versionIds
     * @param year
     * @param type
     * @return
     */
    private BigDecimal getYearNewSupplyAmount(List<String> versionIds, String year, String type) {
        //年度新供金额
        BigDecimal yearNewSupplyAmount = BigDecimal.ZERO;
        //版本集合的签约供货详情数据
        List<SignPlanVersionSupplyDetail> spvsdList = signPlanVersionSupplyDetailMapper.selectByTypeAndIds(versionIds, type, IsDeleteEnum.NO.getKey());
        // 取签约供货详情,有实际取实际，无实际取计划
        List<SignSupplyDetailBO> ssdBOList = switchSignSupplyDetailBO(spvsdList);
        //yaer年签约供货详情
        List<SignSupplyDetailBO> yearSsdBOList = ssdBOList.stream().filter(s -> year.equals(String.valueOf(s.getSupplyDate().getYear()))).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(yearSsdBOList)){
            for(SignSupplyDetailBO bo : yearSsdBOList){
                yearNewSupplyAmount = yearNewSupplyAmount.add(bo.getHouseCommodityValue().add(bo.getNotCommodityValue()));
            }
        }
        return yearNewSupplyAmount;
    }


    /**
     * 获取版本列表中全盘签约数据
     *
     * @param projectId  项目id
     * @param year       年份
     * @param code       筛选条件（业态.产品构成）
     * @param versionIds 签约版本id列表
     * @return
     */
    public List<SignPlanVersionDetailDto> getOverallSignPlanListDate(String projectId, String year, String code, List<String> versionIds) {
        List<SignPlanVersionDetailDto> detailList = new ArrayList<>();
        ProjectByStagesDto projectByStagesDto = projectByStagesMapper.selectByOrgdepid(projectId);
        if (null == projectByStagesDto) {
            throw new MsgException(CodeEnum.PROJECT_ERROR);
        }

        List<SignPlanVersionSupplyDetail> projectList = new ArrayList<>();
        SignPlanVersionSupplyDetail signPlanVersionSupplyDetail = new SignPlanVersionSupplyDetail();
        signPlanVersionSupplyDetail.setId(projectId);
        signPlanVersionSupplyDetail.setSupplyPlanId(projectId);
        signPlanVersionSupplyDetail.setPid("0");
        // 层级类型
        signPlanVersionSupplyDetail.setType(projectByStagesDto.getHrOrgTypeId());
        // 项目名称
        signPlanVersionSupplyDetail.setOrgName(projectByStagesDto.getOrgname());
        projectList.add(signPlanVersionSupplyDetail);
        // 2、
        // 要被查询的日期（若year为空，则查询当年）
        LocalDateTime dateTime;
        if (StringUtils.isEmpty(year)) {
            dateTime = LocalDateTimeUtils.getEarliestMonthDate(LocalDateTime.now());
        } else {
            dateTime = LocalDateTimeUtils.getEarliestMonthDate(year);
        }
        // 3、根据版本计算各版本数据
        List<SignPlanVersionDetailDto> projectDtoList;
        List<SignPlanVersionDetailDto> stageDtoList = new ArrayList<>();
        List<SignPlanVersionDetailDto> grouDtoList = new ArrayList<>();
        List<SignPlanVersionDetailDto> productDtoList = new ArrayList<>();

        String proTypeName = StrUtils.getStrOrgName(code, 0); // 业态
        String productName = StrUtils.getStrOrgName(code, 1); // 产品构成
        for (String versionId : versionIds) {
            List<SignPlanVersionDetailDto> stageDtos;
            List<SignPlanVersionDetailDto> groupDtos;
            List<SignPlanVersionDetailDto> productDtos = new ArrayList<>();

            SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
            // 组合签约计划全路径名称
            String fullName = VersionUtils.getFullName(signPlanVersion);
            // 版本所属时间
            LocalDateTime affiliationMonth = LocalDateTimeUtils.toLocalDateTime(signPlanVersion.getAffiliationMonth());
            // 分期
            List<SignPlanVersionSupplyDetail> stageList = signPlanVersionSupplyDetailMapper.selectByType(
                    versionId, OrgTypeEunm.ORG_STAGE.getKey(), IsDeleteEnum.NO.getKey());
            for (SignPlanVersionSupplyDetail stage : stageList) {
                stage.setOrgName(stage.getOrgName() + "(" + fullName + ")");
                stage.setPid(projectId);
            }
            // 组团
            List<SignPlanVersionSupplyDetail> groupList = signPlanVersionSupplyDetailMapper.selectByType(
                    versionId, OrgTypeEunm.ORG_GROUP.getKey(), IsDeleteEnum.NO.getKey());
            //产品构成
            for (SignPlanVersionSupplyDetail group : groupList) {
                String pid = group.getSupplyPlanId();
                String groupId = group.getOrgId();
                // 产品构成
                List<SignPlanVersionSupplyDetail> signPlanVersionSupplyDetails = signPlanVersionSupplyDetailMapper
                        .selectProductDetail(versionId, pid, OrgTypeEunm.ORG_PRODUCT.getKey(), productName, proTypeName,
                                IsDeleteEnum.NO.getKey());

                // 计算产品构成数据
                if (signPlanVersionSupplyDetails != null) {
                    // 来源供货版本id
                    String sourceSupplyId = signPlanVersion.getSourceSupplyId();
                    //产品构成详情表
                    List<SignPlanVersionDetailDto> productDetailList = calculateProductData(signPlanVersionSupplyDetails, sourceSupplyId, dateTime, affiliationMonth, groupId);
                    productDtos.addAll(productDetailList);
                }
            }

            // 产品构成数据向组团汇总
            groupDtos = signDataSorting(groupList, productDtos, OrgTypeEunm.ORG_PRODUCT.getKey());
            // 组团数据向分期汇总
            stageDtos = signDataSorting(stageList, groupDtos, OrgTypeEunm.ORG_GROUP.getKey());

            productDtoList.addAll(productDtos);
            grouDtoList.addAll(groupDtos);
            stageDtoList.addAll(stageDtos);
        }
        // 4、各分期数据向上汇总到项目上
        projectDtoList = signDataSorting(projectList, stageDtoList, OrgTypeEunm.ORG_STAGE.getKey());

        // 组团排序
        groupOrderBy(grouDtoList,"org_group");
        // 分期排序
        groupOrderBy(stageDtoList,"org_stage");

        detailList.addAll(projectDtoList);
        detailList.addAll(stageDtoList);
        detailList.addAll(grouDtoList);
        detailList.addAll(productDtoList);

        // 单位转换
        detailList = unitConversion(detailList);

        return detailList;
    }


    /**
     * 判断供货计划版本是否在签约中被有效使用
     *
     * @param supplyVersionId 供货版本id
     * @return boolean
     */
    public boolean supplyBoolean(String supplyVersionId) {
        saveLog(LogEventEnum.BROWSE,"签约计划","判断供货计划版本是否在签约中被有效使用","supplyVersionId:"+supplyVersionId);
        List<SignPlanVersion> list = signPlanVersionMapper.supplyBoolean(supplyVersionId, IsDeleteEnum.NO.getKey());
        return null != list && !list.isEmpty();
    }


    /**
     * 货值版本是否在签约中被有效使用
     *
     * @param versionIds 货值版本id列表
     * @return Map<String , Boolean>
     */
    public Map<String, Boolean> priceBoolean(List<String> versionIds) {
        saveLog(LogEventEnum.BROWSE,"签约计划","货值版本是否在签约中被有效使用","versionIds:"+versionIds.toString());
        Map<String, Boolean> priceBooleanMap = new HashMap<>();
        List<String> priceIdsList = signPlanVersionMapper.priceBoolean(versionIds, IsDeleteEnum.NO.getKey());
        for (String versionId : versionIds) {
            if (priceIdsList.contains(versionId)) {
                priceBooleanMap.put(versionId, true);
            } else {
                priceBooleanMap.put(versionId, false);
            }
        }
        return priceBooleanMap;
    }

    /**
     * 查询产品构成列表
     *
     * @param versionId 版本id
     * @return
     */
    @Override
    public List<SignProductVo> getProductList(String versionId) {
        List<SignProductVo> signProductList = new ArrayList<>();

        List<SignPlanVersionSupplyDetail> productList;
        List<SignPlanVersionSupplyDetail> groupList;
        //根据产品构成、经营方式、装修标准、属性标签做分组
        List<SignPlanVersionSupplyDetail> spvsdList = signPlanVersionSupplyDetailMapper.selectOrgCodeList(versionId);
        //产品构成
        productList = spvsdList.stream().filter(s -> s.getType().equals(OrgTypeEunm.ORG_PRODUCT.getKey())).collect(Collectors.toList());
        //组团
        groupList = spvsdList.stream().filter(s -> s.getType().equals(OrgTypeEunm.ORG_GROUP.getKey())).collect(Collectors.toList());
        for (SignPlanVersionSupplyDetail product : productList) {
            String pid = product.getPid();
            //关联出当前产品构成的组团
            SignPlanVersionSupplyDetail group = groupList.stream().filter(s -> s.getSupplyPlanId().equals(pid)).collect(toList()).get(0);

            SignProductVo signProductVo = new SignProductVo();
            signProductVo.setVersionId(versionId);
            signProductVo.setPid(pid);
            signProductVo.setGroupId(group.getOrgId());
            signProductVo.setGroupName(group.getOrgName());
            signProductVo.setProductCode(product.getOrgCode());
            signProductVo.setProductName(product.getOrgName());
            signProductVo.setBusinessType(product.getBusinessType());
            signProductVo.setBusinessTypeName(product.getBusinessTypeName());
            signProductVo.setFreeType(product.getFreeType());
            signProductVo.setFreeTypeName(product.getFreeTypeName());
            signProductVo.setProType(product.getProType());
            signProductVo.setProTypeName(product.getProTypeName());
            signProductList.add(signProductVo);
        }
        return signProductList;
    }

    /**
     * 查询全盘签约概览
     * @param versionId 版本id
     * @param year      年份
     * @param month     月份
     * @return
     */
    @Override
    public List<ProjectProductBO> getProjectProductList(String versionId, String year,String month) {
        String yearMonth = LocalDateTimeUtils.toStrYearMonth(Integer.valueOf(year),Integer.valueOf(month));
        //次年
        String nextYear = String.valueOf(Integer.valueOf(year)+1);

        List<ProjectProductBO> projectProductList = new ArrayList<>();
        //查询版本
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
        //禁用分期 或 全盘最新供货版本未全部可租或不可租售的分期 提示
        MdmStage stage = stageService.selectById(signPlanVersion.getProjectfId());
        if (StageStatusEnum.INACTIVE.getKey().equals(stage.getStatus())) {
            throw new MsgException(CodeEnum.STAGE_INACTIVE_STAGE_ERROR);
        }
        if (!getStageIsSaly(signPlanVersion.getProjectfId())) {
            throw new MsgException(CodeEnum.STAGE_NOT_BUILD_SALY_TYPE_ERROR);
        }
        //项目id
        String projectId = signPlanVersion.getProjectId();
        //查询项目详情
        ProjectByStagesDto projectDto = projectByStagesMapper.selectByOrgdepid(projectId);
        //项目名称
        String projectName = projectDto.getOrgname();

        //查询项目下有效分期列表
        List<String> projectfIds = signPlanVersionMapper.selectProjectfIdsByProjectId(projectId, IsDeleteEnum.NO.getKey());
        for (String projectfId : projectfIds) {
            MdmStage mdmStage = stageService.selectById(projectfId);
            if(StageStatusEnum.INACTIVE.getKey().equals(mdmStage.getStatus())){
                //剔除禁用分期
                continue;
            }
           if(!getStageIsSaly(projectfId)){
               //剔除全盘最新供货版本未全部可租或不可租售的分期
               continue;
           }
            //查询分期详情
            ProjectByStagesDto stageDto = projectByStagesMapper.selectByOrgdepid(projectfId);
            String stageName = stageDto.getOrgname();
            //查询分期下最新版本
            signPlanVersion = signPlanVersionMapper.selectNewesSignVersion(projectfId, IsDeleteEnum.NO.getKey());
            //查询版本签约详情列表
            List<SignPlanVersionDetail> spvdList = signPlanVersionDetailMapper.selectByVersionId(signPlanVersion.getId(), IsDeleteEnum.NO.getKey());

            // 版本所属时间
            String signAffiliationMonth = signPlanVersion.getAffiliationMonth();
            LocalDateTime affiliationMonth = LocalDateTimeUtils.toLocalDateTime(signPlanVersion.getAffiliationMonth());

            //查询版本组团下产品构成Code列表
            List<SignPlanVersionDetail> productList = signPlanVersionDetailMapper.selectCodeByGroup(signPlanVersion.getId(), IsDeleteEnum.NO.getKey());
            for (SignPlanVersionDetail product : productList) {
                ProjectProductBO bo = new ProjectProductBO();

                String groupId = product.getGroupId();
                String productCode = product.getProductCode();
                String businessType = product.getBusinessType();
                String freeType = product.getFreeType();
                String proType = product.getProType();
                //该产品构成签约详情列表
                List<SignPlanVersionDetail> products = spvdList.stream()
                        .filter(s -> s.getGroupId().equals(groupId) && s.getBusinessType().equals(businessType) && s.getProductCode().equals(productCode) && s.getFreeType().equals(freeType) && s.getProType().equals(proType))
                        .collect(Collectors.toList());
                //当月签约数据
                List<SignPlanVersionDetail> yearMonthDetailList = products.stream().filter(s -> s.getSignPlanTime().toString().contains(yearMonth)).collect(Collectors.toList());
                //当年签约数据
                List<SignPlanVersionDetail> yearDetailList = products.stream().filter(s -> s.getSignPlanTime().toString().contains(year)).collect(Collectors.toList());
                //次年签约数据
                List<SignPlanVersionDetail> nextYearDetailList = products.stream().filter(s -> s.getSignPlanTime().toString().contains(nextYear)).collect(Collectors.toList());


                BigDecimal count = BigDecimal.ZERO;
                BigDecimal price = BigDecimal.ZERO;
                BigDecimal yearCount = BigDecimal.ZERO;
                BigDecimal yearPrice = BigDecimal.ZERO;
                BigDecimal nextYearCount = BigDecimal.ZERO;
                BigDecimal nextYearPrice = BigDecimal.ZERO;
                BigDecimal canCount = BigDecimal.ZERO;
                BigDecimal canArea = BigDecimal.ZERO;
                BigDecimal canPrice = BigDecimal.ZERO;
                for (SignPlanVersionDetail detail : yearMonthDetailList) {
                    // 当前年月是否早于编制时间，若早于取签约计划详情数据，否则取签约实际详情数据
                    if (LocalDateTimeUtils.compareDate(detail.getSignPlanTime(), affiliationMonth, 0)) {
                        count = count.add(BigDecimal.valueOf(detail.getEarlySignRoomCountActual().longValue() + detail.getSupplySignRoomCountActual().longValue()));
                        price = price.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
                    } else {
                        count = count.add(detail.getEarlySignRoomCount().add(detail.getSupplySignRoomCount()));
                        price = price.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                    }
                }

                for (SignPlanVersionDetail detail : yearDetailList) {
                    // 当前年月是否早于编制时间，若早于取签约计划详情数据，否则取签约实际详情数据
                    if (LocalDateTimeUtils.compareDate(detail.getSignPlanTime(), affiliationMonth, 0)) {
                        yearCount = yearCount.add(BigDecimal.valueOf(detail.getEarlySignRoomCountActual().longValue() + detail.getSupplySignRoomCountActual().longValue()));
                        yearPrice = yearPrice.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
                    } else {
                        yearCount = yearCount.add(detail.getEarlySignRoomCount().add(detail.getSupplySignRoomCount()));
                        yearPrice = yearPrice.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                    }
                }

                for (SignPlanVersionDetail detail : nextYearDetailList) {
                    // 当前年月是否早于编制时间，若早于取签约计划详情数据，否则取签约实际详情数据
                    if (LocalDateTimeUtils.compareDate(detail.getSignPlanTime(), affiliationMonth, 0)) {
                        nextYearCount = nextYearCount.add(BigDecimal.valueOf(detail.getEarlySignRoomCountActual().longValue() + detail.getSupplySignRoomCountActual().longValue()));
                        nextYearPrice = nextYearPrice.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
                    } else {
                        nextYearCount = nextYearCount.add(detail.getEarlySignRoomCount().add(detail.getSupplySignRoomCount()));
                        nextYearPrice = nextYearPrice.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                    }
                }
                //查询最晚签约时间的一条数据
                SignPlanVersionDetail spvd = products.stream().sorted(Comparator.comparing(SignPlanVersionDetail::getSignPlanTime).reversed())
                        .collect(Collectors.toList()).get(0);

                // 当前年月是否早于编制时间，若早于取签约计划详情数据，否则取签约实际详情数据
                if (LocalDateTimeUtils.compareDate(spvd.getSignPlanTime(), affiliationMonth, 0)) {
                    // 剩余可签金额 = <月初存货>金额 + <本月新供>金额 - <月初存货-本月签约>实际金额 - <本月新供-本月签约>实际金额
                    canPrice = canPrice.add(spvd.getEarlySumPrice().add(spvd.getSupplySumPrice())
                            .subtract(spvd.getEarlySignSumPriceActual())
                            .subtract(spvd.getSupplySignSumPriceActual()));
                    // 剩余可签面积 = <月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>实际面积 - <本月新供-本月签约>实际面积
                    canArea = canArea.add(spvd.getEarlyRoomArea().add(spvd.getSupplyRoomArea())
                            .subtract(spvd.getEarlySignRoomAreaActual())
                            .subtract(spvd.getSupplySignRoomAreaActual()));
                    //剩余可签套数 = <月初存货>剩余可签套数 + <本月新供>剩余可签套数
                    canCount = canCount.add(spvd.getEarlyCanSignRoomCount().add(spvd.getSupplyCanSignRoomCount()));
                }else {
                    // 剩余可签金额 = <月初存货>金额 + <本月新供>金额 - <月初存货-本月签约>计划金额 - <本月新供-本月签约>计划金额
                    canPrice = canPrice.add(spvd.getEarlySumPrice().add(spvd.getSupplySumPrice())
                            .subtract(spvd.getEarlySignSumPrice())
                            .subtract(spvd.getSupplySignSumPrice()));
                    // 剩余可签面积 = <月初存货>面积 + <本月新供>面积 - <月初存货-本月签约>计划面积 - <本月新供-本月签约>计划面积
                    canArea = canArea.add(spvd.getEarlyRoomArea().add(spvd.getSupplyRoomArea())
                            .subtract(spvd.getEarlySignRoomArea())
                            .subtract(spvd.getSupplySignRoomArea()));
                    //剩余可签套数 = <月初存货>剩余可签套数 + <本月新供>剩余可签套数
                    canCount = canCount.add(spvd.getEarlyCanSignRoomCount().add(spvd.getSupplyCanSignRoomCount()));
                }


                bo.setCode(productCode + businessType + freeType + proType);
                bo.setIsParking(product.getIsParking());
                bo.setVersionId(signPlanVersion.getId());
                bo.setProjectName(projectName);
                bo.setStageName(stageName);
                bo.setGroupId(groupId);
                bo.setProductCode(product.getProductCode());
                bo.setProductName(product.getProductName());
                bo.setBusinessType(product.getBusinessType());
                bo.setBusinessTypeName(product.getBusinessTypeName());
                bo.setFreeType(product.getFreeType());
                bo.setFreeTypeName(product.getFreeTypeName());
                bo.setProType(product.getProType());
                bo.setProTypeName(product.getProTypeName());
                bo.setCount(count);
                bo.setPrice(price);
                bo.setYearCount(yearCount);
                bo.setYearPrice(yearPrice);
                bo.setNextYearCount(nextYearCount);
                bo.setNextYearPrice(nextYearPrice);
                bo.setCanCount(canCount);
                bo.setCanArea(canArea);
                bo.setCanPrice(canPrice);
                projectProductList.add(bo);
            }
        }
        return projectProductList;
    }

    /**
     * 获取项目全盘签约计划报表数据
     *
     * @param projectId  项目id
     * @param versionIds 版本id列表
     * @param startAndEndTimeBO 开始与结束时间
     * @return
     */
    @Override
    public List<OverallSignPlanVo> getOverallSignPlanReport(String projectId, List<String> versionIds,StartAndEndTimeBO startAndEndTimeBO) {
        List<OverallSignPlanVo> voList = new ArrayList<>();

        //获取项目信息
        MdmProject project = projectService.selectByProjectId(projectId,IsDeleteEnum.NO.getKey());
        if (null == project) {
            throw new MsgException(CodeEnum.PROJECT_ERROR);
        }
        //项目
        List<SignPlanVersionSupplyDetail> projectVos = new ArrayList<>();
        SignPlanVersionSupplyDetail signPlanVersionSupplyDetail = new SignPlanVersionSupplyDetail();
        signPlanVersionSupplyDetail.setSupplyPlanId(projectId);
        signPlanVersionSupplyDetail.setPid("0");
        signPlanVersionSupplyDetail.setOrgName(project.getProjectName());
        signPlanVersionSupplyDetail.setOrgCode(project.getProjectCode());
        projectVos.add(signPlanVersionSupplyDetail);

        // 根据版本计算各版本数据
        List<OverallSignPlanVo> projectVoList;
        List<OverallSignPlanVo> stageVoList = new ArrayList<>();
        List<OverallSignPlanVo> groupVoList = new ArrayList<>();
        List<OverallSignPlanVo> productVoList = new ArrayList<>();
        OverallSignPlanVo projectVo = new OverallSignPlanVo();
        OverallSignPlanVo stageVo = new OverallSignPlanVo();
        OverallSignPlanVo groupVo = new OverallSignPlanVo();

        for (String versionId : versionIds) {
            List<OverallSignPlanVo> stageVos;
            List<OverallSignPlanVo> groupVos;
            List<OverallSignPlanVo> productVos = new ArrayList<>();

            SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
            String projectfId = signPlanVersion.getProjectfId();

            // 版本所属时间
            LocalDateTime affiliationMonth = LocalDateTimeUtils.toLocalDateTime(signPlanVersion.getAffiliationMonth());
            // 分期
            List<SignPlanVersionSupplyDetail> stageList = signPlanVersionSupplyDetailMapper.selectByType(
                    versionId, OrgTypeEunm.ORG_STAGE.getKey(), IsDeleteEnum.NO.getKey());
            for (SignPlanVersionSupplyDetail stage : stageList) {
                stage.setPid(projectId);
            }
            //分期名称
            for (SignPlanVersionSupplyDetail stage : stageList) {
                stage.setOrgName(stage.getOrgName());
                stage.setPid(projectId);
            }

            // 组团
            List<SignPlanVersionSupplyDetail> groupList = signPlanVersionSupplyDetailMapper.selectByType(
                    versionId, OrgTypeEunm.ORG_GROUP.getKey(), IsDeleteEnum.NO.getKey());
            //产品构成
            for (SignPlanVersionSupplyDetail group : groupList) {
                String pid = group.getSupplyPlanId();
                String groupId = group.getOrgId();
                // 产品构成
                List<SignPlanVersionSupplyDetail> signPlanVersionSupplyDetails = signPlanVersionSupplyDetailMapper
                        .selectProductDetail(versionId, pid, OrgTypeEunm.ORG_PRODUCT.getKey(), null, null,
                                IsDeleteEnum.NO.getKey());

                if (signPlanVersionSupplyDetails == null) {
                    return null;
                }

                // 计算产品构成数据
                productVos.addAll(calculateOverallProduct(signPlanVersionSupplyDetails, signPlanVersion.getSourceSupplyId(),startAndEndTimeBO, affiliationMonth, groupId));
            }
            // 产品构成数据向组团汇总
            groupVos = signDataSummarization(groupList, productVos, OrgTypeEunm.ORG_PRODUCT.getKey(),startAndEndTimeBO);
            // 组团数据向分期汇总
            stageVos = signDataSummarization(stageList, groupVos, OrgTypeEunm.ORG_GROUP.getKey(),startAndEndTimeBO);
            //数据补充
            StagePartNers stagePartners = stagePartNersService.getStagePartnersByProjectFid(projectfId);
            DmDmValueReport dmDmValueReport = signPlanVersionMapper.selectDmValueReportByStageId(signPlanVersion.getProjectfId(),signPlanVersion.getAffiliationMonth());
            for (OverallSignPlanVo stage :stageVos){
                //分期编码
                stage.setStageCode(stagePartners.getStageCode());
                //旭辉权益比例
                stage.setEquityRatio(NumberUtils.accuracyBigDecimal(stagePartners.getEquityRatio(),2).toString());
                //营销操盘方式
                stage.setOpmarketing(stagePartners.getIsOpMarketing());
                //是否并表
                stage.setIsAndTable(stagePartners.getPartNerIsAndTable() == null ? 0 : Integer.valueOf(stagePartners.getPartNerIsAndTable()));
                if(null != dmDmValueReport){
                    //现房销售
                    stage.setExistingHouses(dmDmValueReport.getIsExistRoomSale());
                }
            }

            productVoList.addAll(productVos);
            groupVoList.addAll(groupVos);
            stageVoList.addAll(stageVos);
        }
        // 4、各分期数据向上汇总到项目上
        projectVoList = signDataSummarization(projectVos, stageVoList, OrgTypeEunm.ORG_STAGE.getKey(),startAndEndTimeBO);
        //全签约计划数据排序
        overallSignPlanVoSort(groupVoList, OrgTypeEunm.ORG_GROUP.getKey());
        overallSignPlanVoSort(stageVoList, OrgTypeEunm.ORG_STAGE.getKey());

        voList.addAll(projectVoList);
        //数据补充
        for(OverallSignPlanVo stage : stageVoList){
            stage.setProjectName(project.getProjectName());
            stage.setProjectCode(project.getProjectCode());
            voList.add(stage);
            for(OverallSignPlanVo group : groupVoList){
                if(stage.getId().equals(group.getPid())){
                    group.setProjectName(project.getProjectName());
                    group.setProjectCode(project.getProjectCode());
                    group.setStageName(stage.getStageName());
                    group.setStageCode(stage.getStageCode());
                    group.setEquityRatio(stage.getEquityRatio());
                    group.setOpmarketing(stage.getOpmarketing());
                    group.setIsAndTable(stage.getIsAndTable());
                    group.setExistingHouses(stage.getExistingHouses());
                    voList.add(group);
                    for(OverallSignPlanVo product : productVoList){
                        if(group.getId().equals(product.getPid())){
                            product.setProjectName(project.getProjectName());
                            product.setProjectCode(project.getProjectCode());
                            product.setStageName(stage.getStageName());
                            product.setStageCode(stage.getStageCode());
                            product.setEquityRatio(stage.getEquityRatio());
                            product.setOpmarketing(stage.getOpmarketing());
                            product.setIsAndTable(stage.getIsAndTable());
                            product.setExistingHouses(stage.getExistingHouses());
                            product.setGroupName(group.getGroupName());
                            voList.add(product);
                        }
                    }
                }
            }
        }
        return voList;
    }

    /**
     * 全签约计划数据排序
     * @param groupVoList 全签约计划数据
     * @param orgType 数据层级类型
     */
    private void overallSignPlanVoSort(List<OverallSignPlanVo> VoList, String orgType) {
        if(orgType.equals(OrgTypeEunm.ORG_GROUP.getKey())){
            //组团排序
            VoList.sort((a, b) -> {
                String aName = a.getGroupName();
                String bName = b.getGroupName();

                String[] aStr = aName.split("组团");
                String[] bStr = bName.split("组团");

                if (aStr[0].equals("车位")) {
                    aStr[0] = "10000000";
                }
                if (bStr[0].equals("车位")) {
                    bStr[0] = "10000000";
                }

                String aValue = NumberEnum.getKeyByValue(aStr[0]);
                String bValue = NumberEnum.getKeyByValue(bStr[0]);

                return aValue == null ? 1 : bValue == null ? -1 : Integer.valueOf(aValue) - Integer.valueOf(bValue);
            });
        }else if(orgType.equals(OrgTypeEunm.ORG_STAGE.getKey())){
            //分期排序
            VoList.sort((a,b) ->{
                String aName = a.getStageName();
                String bName = b.getStageName();

                String[] aStr = aName.split("期");
                String[] bStr = bName.split("期");

                String aValue = NumberEnum.getKeyByValue(aStr[0]);
                String bValue = NumberEnum.getKeyByValue(bStr[0]);

                return aValue == null ? 1 : bValue == null ? -1 : Integer.valueOf(aValue) - Integer.valueOf(bValue);
            });

        }
    }

    /**
     * 产品构成签约数据向上归
     *
     * @param detailList    汇总集合
     * @param vos       被汇总的集合
     * @param orgType    被汇总数据的层级类型
     * @param startAndEndTimeBO 开始时间与结束时间
     * @return
     */
    public List<OverallSignPlanVo> signDataSummarization(List<SignPlanVersionSupplyDetail> detailList,List<OverallSignPlanVo> vos, String orgType,StartAndEndTimeBO startAndEndTimeBO) {

        List<OverallSignPlanVo> voList = new ArrayList<>();

        for (SignPlanVersionSupplyDetail detail : detailList) {
            OverallSignPlanVo vo = new OverallSignPlanVo();
            String id = detail.getSupplyPlanId();
            if (orgType.equals(OrgTypeEunm.ORG_PRODUCT.getKey())) {
                //组团名称
                vo.setGroupName(detail.getOrgName());
            }else if (orgType.equals(OrgTypeEunm.ORG_GROUP.getKey())) {
                //分期名称
                vo.setStageName(detail.getOrgName());
            }else if (orgType.equals(OrgTypeEunm.ORG_STAGE.getKey())) {
                //项目名称
                vo.setProjectName(detail.getOrgName());
                vo.setProjectCode(detail.getOrgCode());
            }
            vo.setId(id);
            vo.setPid(detail.getPid());

            //全盘供货与签约
            SupplyAndSignVo supplyAndSignVo = supplyAndSignSummarization(id,vos,orgType);
            //月度全盘签约
            List<MonthSignVo> monthSignVoList = monthSignSummarization(id,vos,orgType,startAndEndTimeBO);
            //年度全盘签约
            List<YearSignVo> yearSignVoList = yearSignSummarization(id,vos,orgType,startAndEndTimeBO);

            vo.setSupplyAndSignVo(supplyAndSignVo);
            vo.setMonthSignVo(monthSignVoList);
            vo.setYearSignVo(yearSignVoList);
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 年度全盘签约汇总
     * @param id
     * @param vos 被汇总数据集合
     * @param orgType 被汇总数据的层级类型
     * @param startAndEndTimeBO 开始时间与结束时间
     * @return
     */
    private List<YearSignVo> yearSignSummarization(String id, List<OverallSignPlanVo> vos, String orgType,StartAndEndTimeBO startAndEndTimeBO) {
        List<YearSignVo> yearSignVoList = new ArrayList<>();

        //开始时间
        LocalDateTime startDate = startAndEndTimeBO.getStartDate();
        //结束时间
        LocalDateTime endDate = startAndEndTimeBO.getEndDate();
        List<String> yearList = new ArrayList<>();
        int earliestyear = startDate.getYear();
        int latestyear = endDate.getYear();
        for (int year = earliestyear; year <= latestyear; year++) {
            yearList.add(String.valueOf(year));
        }

        for (String year : yearList){
            YearSignVo yearSignVo = new YearSignVo();
            //<年初存货>套数/车位数
            String earlyNum = NumberUtils.stringInitial(2,5);
            //<年初存货>面积
            String earlyArea = NumberUtils.stringInitial(1,4);
            //<年初存货>金额
            String earlyPrice = NumberUtils.stringInitial(3,4);
            //<年初存货>签约实际 套数/车位数
            String earlySignActualNum = NumberUtils.stringInitial(2,5);
            //<年初存货>签约实际面积
            String earlySignActualArea = NumberUtils.stringInitial(1,4);
            //<年初存货>签约实际金额
            String earlySignActualPrice = NumberUtils.stringInitial(3,4);

            //<年度新供>供货计划套数/车位数
            String supplyPlanNum = NumberUtils.stringInitial(2,5);
            //<年度新供>供货计划面积
            String supplyPlanArea = NumberUtils.stringInitial(1,4);
            //<年度新供>供货计划金额
            String supplyPlanPrice = NumberUtils.stringInitial(3,4);
            //<年度新供>供货实际套数/车位数
            String supplyActualNum = NumberUtils.stringInitial(2,5);
            //<年度新供>供货实际面积
            String supplyActualArea = NumberUtils.stringInitial(1,4);
            //<年度新供>供货实际金额
            String supplyActualPrice = NumberUtils.stringInitial(3,4);
            //<年度新供>签约实际 套数/车位数
            String supplySignActualNum = NumberUtils.stringInitial(2,5);
            //<年度新供>签约实际面积
            String supplySignActualArea = NumberUtils.stringInitial(1,4);
            //<年度新供>签约实际金额
            String supplySignActualPrice = NumberUtils.stringInitial(3,4);

            //<年度综合>可售 套数/车位数
            String syntheticalAvailableNum = NumberUtils.stringInitial(2,5);
            //<年度综合>可售面积
            String syntheticalAvailableArea = NumberUtils.stringInitial(1,4);
            //<年度综合>可售金额
            String syntheticalAvailablePrice = NumberUtils.stringInitial(3,4);
            // 年度签约目标套数
            String yearSigningTargetCount = NumberUtils.stringInitial(2,5);
            // 年度签约目标面积
            String yearSigningTargetArea = NumberUtils.stringInitial(1,4);
            // 年度签约目标金额
            String yearSigningTargetAmount = NumberUtils.stringInitial(3,4);
            //<年度综合>签约计划 套数/车位数
            String syntheticalSignPlanNum = NumberUtils.stringInitial(2,5);
            //<年度综合>签约计划面积
            String syntheticalSignPlanArea = NumberUtils.stringInitial(1,4);
            //<年度综合>签约计划金额
            String syntheticalSignPlanPrice = NumberUtils.stringInitial(3,4);
            //<年度综合>签约实际 套数/车位数
            String syntheticalSignActualNum = NumberUtils.stringInitial(2,5);
            //<年度综合>签约实际面积
            String syntheticalSignActualArea = NumberUtils.stringInitial(1,4);
            //<年度综合>签约实际金额
            String syntheticalSignActualPrice = NumberUtils.stringInitial(3,4);


            if (orgType.equals(OrgTypeEunm.ORG_PRODUCT.getKey())) {
                for (OverallSignPlanVo vo : vos) {
                    if (vo.getPid().equals(id)) {
                        int isParking = vo.getIsParking();
                        YearSignVo yearVo = vo.getYearSignVo().stream().filter(s -> s.getYear().equals(year)).collect(toList()).get(0);

                        earlyNum = NumberUtils.numAddcalculate(earlyNum,yearVo.getEarlyNum(),isParking);
                        earlyArea = String.valueOf(new BigDecimal(earlyArea).add(new BigDecimal(yearVo.getEarlyArea())));
                        earlyPrice = NumberUtils.priceAddcalculate(earlyPrice,yearVo.getEarlyPrice(),isParking);
                        earlySignActualNum = NumberUtils.numAddcalculate(earlySignActualNum,yearVo.getEarlySignActualNum(),isParking);
                        earlySignActualArea = String.valueOf(new BigDecimal(earlySignActualArea).add(new BigDecimal(yearVo.getEarlySignActualArea())));
                        earlySignActualPrice = NumberUtils.priceAddcalculate(earlySignActualPrice,yearVo.getEarlySignActualPrice(),isParking);
                        supplyPlanNum = NumberUtils.numAddcalculate(supplyPlanNum,yearVo.getSupplyPlanNum(),isParking);
                        supplyPlanArea = String.valueOf(new BigDecimal(supplyPlanArea).add(new BigDecimal(yearVo.getSupplyPlanArea())));
                        supplyPlanPrice = NumberUtils.priceAddcalculate(supplyPlanPrice,yearVo.getSupplyPlanPrice(),isParking);
                        supplyActualNum = NumberUtils.numAddcalculate(supplyActualNum,yearVo.getSupplyActualNum(),isParking);
                        supplyActualArea = String.valueOf(new BigDecimal(supplyActualArea).add(new BigDecimal(yearVo.getSupplyActualArea())));
                        supplyActualPrice = NumberUtils.priceAddcalculate(supplyActualPrice,yearVo.getSupplyActualPrice(),isParking);
                        supplySignActualNum = NumberUtils.numAddcalculate(supplySignActualNum,yearVo.getSupplySignActualNum(),isParking);
                        supplySignActualArea = String.valueOf(new BigDecimal(supplySignActualArea).add(new BigDecimal(yearVo.getSupplySignActualArea())));
                        supplySignActualPrice = NumberUtils.priceAddcalculate(supplySignActualPrice,yearVo.getSupplySignActualPrice(),isParking);
                        syntheticalAvailableNum = NumberUtils.numAddcalculate(syntheticalAvailableNum,yearVo.getSyntheticalAvailableNum(),isParking);
                        syntheticalAvailableArea = String.valueOf(new BigDecimal(syntheticalAvailableArea).add(new BigDecimal(yearVo.getSyntheticalAvailableArea())));
                        syntheticalAvailablePrice = NumberUtils.priceAddcalculate(syntheticalAvailablePrice,yearVo.getSyntheticalAvailablePrice(),isParking);
                        yearSigningTargetCount = NumberUtils.numAddcalculate(yearSigningTargetCount,yearVo.getYearSigningTargetCount(),isParking);
                        yearSigningTargetArea = String.valueOf(new BigDecimal(yearSigningTargetArea).add(new BigDecimal(yearVo.getYearSigningTargetArea())));
                        yearSigningTargetAmount = NumberUtils.priceAddcalculate(yearSigningTargetAmount,yearVo.getYearSigningTargetAmount(),isParking);
                        syntheticalSignPlanNum = NumberUtils.numAddcalculate(syntheticalSignPlanNum,yearVo.getSyntheticalSignPlanNum(),isParking);
                        syntheticalSignPlanArea = String.valueOf(new BigDecimal(syntheticalSignPlanArea).add(new BigDecimal(yearVo.getSyntheticalSignPlanArea())));
                        syntheticalSignPlanPrice = NumberUtils.priceAddcalculate(syntheticalSignPlanPrice,yearVo.getSyntheticalSignPlanPrice(),isParking);
                        syntheticalSignActualNum = NumberUtils.numAddcalculate(syntheticalSignActualNum,yearVo.getSyntheticalSignActualNum(),isParking);
                        syntheticalSignActualArea = String.valueOf(new BigDecimal(syntheticalSignActualArea).add(new BigDecimal(yearVo.getSyntheticalSignActualArea())));
                        syntheticalSignActualPrice = NumberUtils.priceAddcalculate(syntheticalSignActualPrice,yearVo.getSyntheticalSignActualPrice(),isParking);

                    }
                }
            }else {
                for (OverallSignPlanVo vo : vos) {
                    if (vo.getPid().equals(id)) {
                        YearSignVo yearVo = vo.getYearSignVo().stream().filter(s -> s.getYear().equals(year)).collect(toList()).get(0);

                        earlyNum = NumberUtils.numAdd(earlyNum,yearVo.getEarlyNum());
                        earlyArea = String.valueOf(new BigDecimal(earlyArea).add(new BigDecimal(yearVo.getEarlyArea())));
                        earlyPrice = NumberUtils.priceAdd(earlyPrice,yearVo.getEarlyPrice());
                        earlySignActualNum = NumberUtils.numAdd(earlySignActualNum,yearVo.getEarlySignActualNum());
                        earlySignActualArea = String.valueOf(new BigDecimal(earlySignActualArea).add(new BigDecimal(yearVo.getEarlySignActualArea())));
                        earlySignActualPrice = NumberUtils.priceAdd(earlySignActualPrice,yearVo.getEarlySignActualPrice());
                        supplyPlanNum = NumberUtils.numAdd(supplyPlanNum,yearVo.getSupplyPlanNum());
                        supplyPlanArea = String.valueOf(new BigDecimal(supplyPlanArea).add(new BigDecimal(yearVo.getSupplyPlanArea())));
                        supplyPlanPrice = NumberUtils.priceAdd(supplyPlanPrice,yearVo.getSupplyPlanPrice());
                        supplyActualNum = NumberUtils.numAdd(supplyActualNum,yearVo.getSupplyActualNum());
                        supplyActualArea = String.valueOf(new BigDecimal(supplyActualArea).add(new BigDecimal(yearVo.getSupplyActualArea())));
                        supplyActualPrice = NumberUtils.priceAdd(supplyActualPrice,yearVo.getSupplyActualPrice());
                        supplySignActualNum = NumberUtils.numAdd(supplySignActualNum,yearVo.getSupplySignActualNum());
                        supplySignActualArea = String.valueOf(new BigDecimal(supplySignActualArea).add(new BigDecimal(yearVo.getSupplySignActualArea())));
                        supplySignActualPrice = NumberUtils.priceAdd(supplySignActualPrice,yearVo.getSupplySignActualPrice());
                        syntheticalAvailableNum = NumberUtils.numAdd(syntheticalAvailableNum,yearVo.getSyntheticalAvailableNum());
                        syntheticalAvailableArea = String.valueOf(new BigDecimal(syntheticalAvailableArea).add(new BigDecimal(yearVo.getSyntheticalAvailableArea())));
                        syntheticalAvailablePrice = NumberUtils.priceAdd(syntheticalAvailablePrice,yearVo.getSyntheticalAvailablePrice());
                        yearSigningTargetCount = NumberUtils.numAdd(yearSigningTargetCount,yearVo.getYearSigningTargetCount());
                        yearSigningTargetArea = String.valueOf(new BigDecimal(yearSigningTargetArea).add(new BigDecimal(yearVo.getYearSigningTargetArea())));
                        yearSigningTargetAmount = NumberUtils.priceAdd(yearSigningTargetAmount,yearVo.getYearSigningTargetAmount());
                        syntheticalSignPlanNum = NumberUtils.numAdd(syntheticalSignPlanNum,yearVo.getSyntheticalSignPlanNum());
                        syntheticalSignPlanArea = String.valueOf(new BigDecimal(syntheticalSignPlanArea).add(new BigDecimal(yearVo.getSyntheticalSignPlanArea())));
                        syntheticalSignPlanPrice = NumberUtils.priceAdd(syntheticalSignPlanPrice,yearVo.getSyntheticalSignPlanPrice());
                        syntheticalSignActualNum = NumberUtils.numAdd(syntheticalSignActualNum,yearVo.getSyntheticalSignActualNum());
                        syntheticalSignActualArea = String.valueOf(new BigDecimal(syntheticalSignActualArea).add(new BigDecimal(yearVo.getSyntheticalSignActualArea())));
                        syntheticalSignActualPrice = NumberUtils.priceAdd(syntheticalSignActualPrice,yearVo.getSyntheticalSignActualPrice());
                    }
                }
            }
            //<年初存货>均价
            String earlyAvgPrice = NumberUtils.priceDiv(earlyPrice,earlyArea,earlyNum);
            //<年初存货>签约实际均价
            String earlySignActualAvgPrice = NumberUtils.priceDiv(earlySignActualPrice,earlySignActualArea,earlySignActualNum);
            //<年初存货>实际去化率
            String earlyActualRemovalRate = NumberUtils.pricePercentage(earlySignActualPrice,earlyPrice);
            //<年度新供>供货计划均价
            String supplyPlanAvgPrice = NumberUtils.priceDiv(supplyPlanPrice,supplyPlanArea,supplyPlanNum);
            //<年度新供>供货实际均价
            String supplyActualAvgPrice = NumberUtils.priceDiv(supplyActualPrice,supplyActualArea,supplyActualNum);
            //<年度新供>签约实际均价
            String supplySignActualAvgPrice = NumberUtils.priceDiv(supplySignActualPrice,supplySignActualArea,supplySignActualNum);
            //<年度新供>供货完成率
            String supplyFillRate = NumberUtils.pricePercentage(supplyActualPrice,supplyPlanPrice);
            //<年度新供>实际去化率
            String supplyActualRemovalRate = NumberUtils.pricePercentage(supplySignActualPrice,supplyActualPrice);
            //<年度综合>可售均价
            String syntheticalAvailableAvgPrice = NumberUtils.priceDiv(syntheticalAvailablePrice,syntheticalAvailableArea,syntheticalAvailableNum);
            // 年度签约目标均价
            String yearSigningTargetAvgPrice = NumberUtils.priceDiv(yearSigningTargetAmount,yearSigningTargetArea,yearSigningTargetCount);
            //<年度综合>签约计划均价
            String syntheticalSignPlanAvgPrice = NumberUtils.priceDiv(syntheticalSignPlanPrice,syntheticalSignPlanArea,syntheticalSignPlanNum);
            //<年度综合>签约实际均价
            String syntheticalSignActualAvgPrice = NumberUtils.priceDiv(syntheticalSignActualPrice,syntheticalSignActualArea,syntheticalSignActualNum);
            //<年度综合>签约完成率
            String syntheticalSignFillRate = NumberUtils.pricePercentage(syntheticalSignActualPrice,syntheticalSignPlanPrice);
            //<年度综合>实际去化率
            String syntheticalActualRemovalRate = NumberUtils.pricePercentage(syntheticalSignActualPrice,syntheticalAvailablePrice);
            //<年度综合>计划去化率
            String syntheticalPlanRemovalRate = NumberUtils.pricePercentage(syntheticalSignPlanPrice,syntheticalAvailablePrice);

            yearSignVo.setYear(year);
            yearSignVo.setEarlyNum(String.valueOf(earlyNum));
            yearSignVo.setEarlyArea(String.valueOf(earlyArea));
            yearSignVo.setEarlyAvgPrice(String.valueOf(earlyAvgPrice));
            yearSignVo.setEarlyPrice(String.valueOf(earlyPrice));
            yearSignVo.setEarlySignActualNum(String.valueOf(earlySignActualNum));
            yearSignVo.setEarlySignActualArea(String.valueOf(earlySignActualArea));
            yearSignVo.setEarlySignActualAvgPrice(String.valueOf(earlySignActualAvgPrice));
            yearSignVo.setEarlySignActualPrice(String.valueOf(earlySignActualPrice));
            yearSignVo.setEarlyActualRemovalRate(String.valueOf(earlyActualRemovalRate));
            yearSignVo.setSupplyPlanNum(String.valueOf(supplyPlanNum));
            yearSignVo.setSupplyPlanArea(String.valueOf(supplyPlanArea));
            yearSignVo.setSupplyPlanAvgPrice(String.valueOf(supplyPlanAvgPrice));
            yearSignVo.setSupplyPlanPrice(String.valueOf(supplyPlanPrice));
            yearSignVo.setSupplyActualNum(String.valueOf(supplyActualNum));
            yearSignVo.setSupplyActualArea(String.valueOf(supplyActualArea));
            yearSignVo.setSupplyActualAvgPrice(String.valueOf(supplyActualAvgPrice));
            yearSignVo.setSupplyActualPrice(String.valueOf(supplyActualPrice));
            yearSignVo.setSupplyFillRate(String.valueOf(supplyFillRate));
            yearSignVo.setSupplySignActualNum(String.valueOf(supplySignActualNum));
            yearSignVo.setSupplySignActualArea(String.valueOf(supplySignActualArea));
            yearSignVo.setSupplySignActualAvgPrice(String.valueOf(supplySignActualAvgPrice));
            yearSignVo.setSupplySignActualPrice(String.valueOf(supplySignActualPrice));
            yearSignVo.setSupplyActualRemovalRate(String.valueOf(supplyActualRemovalRate));
            yearSignVo.setSyntheticalAvailableNum(String.valueOf(syntheticalAvailableNum));
            yearSignVo.setSyntheticalAvailableArea(String.valueOf(syntheticalAvailableArea));
            yearSignVo.setSyntheticalAvailableAvgPrice(String.valueOf(syntheticalAvailableAvgPrice));
            yearSignVo.setSyntheticalAvailablePrice(String.valueOf(syntheticalAvailablePrice));
            yearSignVo.setYearSigningTargetCount(String.valueOf(yearSigningTargetCount));
            yearSignVo.setYearSigningTargetArea(String.valueOf(yearSigningTargetArea));
            yearSignVo.setYearSigningTargetAvgPrice(String.valueOf(yearSigningTargetAvgPrice));
            yearSignVo.setYearSigningTargetAmount(String.valueOf(yearSigningTargetAmount));
            yearSignVo.setSyntheticalSignPlanNum(String.valueOf(syntheticalSignPlanNum));
            yearSignVo.setSyntheticalSignPlanArea(String.valueOf(syntheticalSignPlanArea));
            yearSignVo.setSyntheticalSignPlanAvgPrice(String.valueOf(syntheticalSignPlanAvgPrice));
            yearSignVo.setSyntheticalSignPlanPrice(String.valueOf(syntheticalSignPlanPrice));
            yearSignVo.setSyntheticalSignActualNum(String.valueOf(syntheticalSignActualNum));
            yearSignVo.setSyntheticalSignActualArea(String.valueOf(syntheticalSignActualArea));
            yearSignVo.setSyntheticalSignActualAvgPrice(String.valueOf(syntheticalSignActualAvgPrice));
            yearSignVo.setSyntheticalSignActualPrice(String.valueOf(syntheticalSignActualPrice));
            yearSignVo.setSyntheticalSignFillRate(String.valueOf(syntheticalSignFillRate));
            yearSignVo.setSyntheticalActualRemovalRate(String.valueOf(syntheticalActualRemovalRate));
            yearSignVo.setSyntheticalPlanRemovalRate(String.valueOf(syntheticalPlanRemovalRate));

            yearSignVoList.add(yearSignVo);
        }
        return yearSignVoList;
    }

    /**
     * 月度全盘签约汇总
     * @param id
     * @param vos 被汇总数据集合
     * @param orgType 被汇总数据的层级类型
     * @param startAndEndTimeBO 开始时间与结束时间
     * @return
     */
    private List<MonthSignVo> monthSignSummarization(String id, List<OverallSignPlanVo> vos, String orgType,StartAndEndTimeBO startAndEndTimeBO){
        List<MonthSignVo> monthSignVoList = new ArrayList<>();

        //开始时间
        LocalDateTime startDate = startAndEndTimeBO.getStartDate();
        //结束时间
        LocalDateTime endDate = startAndEndTimeBO.getEndDate();
        for (LocalDateTime time = startDate; LocalDateTimeUtils.compareDate(time, endDate,1); time = LocalDateTimeUtils.addDate(time)) {
            MonthSignVo monthSignVo = new MonthSignVo();
            String yearMonth = LocalDateTimeUtils.toStrYearMonth(time.getYear(), time.getMonthValue());

            //<月初存货>套数/车位数
            String earlyNum = NumberUtils.stringInitial(2,5);
            //<月初存货>面积
            String earlyArea = NumberUtils.stringInitial(1,4);
            //<月初存货>金额
            String earlyPrice = NumberUtils.stringInitial(3,4);
            //<月初存货>签约计划 套数/车位数
            String earlySignPlanNum = NumberUtils.stringInitial(2,5);
            //<月初存货>签约计划面积
            String earlySignPlanArea = NumberUtils.stringInitial(1,4);
            //<月初存货>签约计划金额
            String earlySignPlanPrice = NumberUtils.stringInitial(3,4);
            //<月初存货>签约实际 套数/车位数
            String earlySignActualNum = NumberUtils.stringInitial(2,5);
            //<月初存货>签约实际面积
            String earlySignActualArea = NumberUtils.stringInitial(1,4);
            //<月初存货>签约实际金额
            String earlySignActualPrice = NumberUtils.stringInitial(3,4);

            //<月度新供>供货计划套数/车位数
            String supplyPlanNum = NumberUtils.stringInitial(2,5);
            //<月度新供>供货计划面积
            String supplyPlanArea = NumberUtils.stringInitial(1,4);
            //<月度新供>供货计划金额
            String supplyPlanPrice = NumberUtils.stringInitial(3,4);
            //<月度新供>供货实际套数/车位数
            String supplyActualNum = NumberUtils.stringInitial(2,5);
            //<月度新供>供货实际面积
            String supplyActualArea = NumberUtils.stringInitial(1,4);
            //<月度新供>供货实际金额
            String supplyActualPrice = NumberUtils.stringInitial(3,4);
            //<月度新供>签约计划 套数/车位数
            String supplySignPlanNum = NumberUtils.stringInitial(2,5);
            //<月度新供>签约计划面积
            String supplySignPlanArea = NumberUtils.stringInitial(1,4);
            //<月度新供>签约计划金额
            String supplySignPlanPrice = NumberUtils.stringInitial(3,4);
            //<月度新供>签约实际 套数/车位数
            String supplySignActualNum = NumberUtils.stringInitial(2,5);
            //<月度新供>签约实际面积
            String supplySignActualArea = NumberUtils.stringInitial(1,4);
            //<月度新供>签约实际金额
            String supplySignActualPrice = NumberUtils.stringInitial(3,4);

            //<月度综合>可售 套数/车位数
            String syntheticalAvailableNum = NumberUtils.stringInitial(2,5);
            //<月度综合>可售面积
            String syntheticalAvailableArea = NumberUtils.stringInitial(1,4);;
            //<月度综合>可售金额
            String syntheticalAvailablePrice = NumberUtils.stringInitial(3,4);
            //<月度综合>签约计划 套数/车位数
            String syntheticalSignPlanNum = NumberUtils.stringInitial(2,5);
            //<月度综合>签约计划面积
            String syntheticalSignPlanArea = NumberUtils.stringInitial(1,4);
            //<月度综合>签约计划金额
            String syntheticalSignPlanPrice = NumberUtils.stringInitial(3,4);
            //<月度综合>签约实际 套数/车位数
            String syntheticalSignActualNum = NumberUtils.stringInitial(2,5);
            //<月度综合>签约实际面积
            String syntheticalSignActualArea = NumberUtils.stringInitial(1,4);
            //<月度综合>签约实际金额
            String syntheticalSignActualPrice = NumberUtils.stringInitial(3,4);

            if (orgType.equals(OrgTypeEunm.ORG_PRODUCT.getKey())) {
                for (OverallSignPlanVo vo :vos){
                    if(vo.getPid().equals(id)){
                        int isParking = vo.getIsParking();
                        List<MonthSignVo> collect = vo.getMonthSignVo().stream().filter(s -> s.getYearMonth().equals(yearMonth)).collect(toList());
                        if(CollectionUtils.isEmpty(collect)){
                            continue;
                        }
                        MonthSignVo monthVo = vo.getMonthSignVo().stream().filter(s -> s.getYearMonth().equals(yearMonth)).collect(toList()).get(0);

                        earlyNum = NumberUtils.numAddcalculate(earlyNum,monthVo.getEarlyNum(),isParking);
                        earlyArea = String.valueOf(new BigDecimal(earlyArea).add(new BigDecimal(monthVo.getEarlyArea())));
                        earlyPrice = NumberUtils.priceAddcalculate(earlyPrice,monthVo.getEarlyPrice(),isParking);
                        earlySignPlanNum = NumberUtils.numAddcalculate(earlySignPlanNum,monthVo.getEarlySignPlanNum(),isParking);
                        earlySignPlanArea = String.valueOf(new BigDecimal(earlySignPlanArea).add(new BigDecimal(monthVo.getEarlySignPlanArea())));
                        earlySignPlanPrice = NumberUtils.priceAddcalculate(earlySignPlanPrice,monthVo.getEarlySignPlanPrice(),isParking);
                        earlySignActualNum = NumberUtils.numAddcalculate(earlySignActualNum,monthVo.getEarlySignActualNum(),isParking);
                        earlySignActualArea = String.valueOf(new BigDecimal(earlySignActualArea).add(new BigDecimal(monthVo.getEarlySignActualArea())));
                        earlySignActualPrice = NumberUtils.priceAddcalculate(earlySignActualPrice,monthVo.getEarlySignActualPrice(),isParking);
                        supplyPlanNum = NumberUtils.numAddcalculate(supplyPlanNum,monthVo.getSupplyPlanNum(),isParking);
                        supplyPlanArea = String.valueOf(new BigDecimal(supplyPlanArea).add(new BigDecimal(monthVo.getSupplyPlanArea())));
                        supplyPlanPrice = NumberUtils.priceAddcalculate(supplyPlanPrice,monthVo.getSupplyPlanPrice(),isParking);
                        supplyActualNum = NumberUtils.numAddcalculate(supplyActualNum,monthVo.getSupplyActualNum(),isParking);
                        supplyActualArea = String.valueOf(new BigDecimal(supplyActualArea).add(new BigDecimal(monthVo.getSupplyActualArea())));
                        supplyActualPrice = NumberUtils.priceAddcalculate(supplyActualPrice,monthVo.getSupplyActualPrice(),isParking);
                        supplySignPlanNum = NumberUtils.numAddcalculate(supplySignPlanNum,monthVo.getSupplySignPlanNum(),isParking);
                        supplySignPlanArea = String.valueOf(new BigDecimal(supplySignPlanArea).add(new BigDecimal(monthVo.getSupplySignPlanArea())));
                        supplySignPlanPrice = NumberUtils.priceAddcalculate(supplySignPlanPrice,monthVo.getSupplySignPlanPrice(),isParking);
                        supplySignActualNum = NumberUtils.numAddcalculate(supplySignActualNum,monthVo.getSupplySignActualNum(),isParking);
                        supplySignActualArea = String.valueOf(new BigDecimal(supplySignActualArea).add(new BigDecimal(monthVo.getSupplySignActualArea())));
                        supplySignActualPrice = NumberUtils.priceAddcalculate(supplySignActualPrice,monthVo.getSupplySignActualPrice(),isParking);
                        syntheticalAvailableNum = NumberUtils.numAddcalculate(syntheticalAvailableNum,monthVo.getSyntheticalAvailableNum(),isParking);
                        syntheticalAvailableArea = String.valueOf(new BigDecimal(syntheticalAvailableArea).add(new BigDecimal(monthVo.getSyntheticalAvailableArea())));
                        syntheticalAvailablePrice = NumberUtils.priceAddcalculate(syntheticalAvailablePrice,monthVo.getSyntheticalAvailablePrice(),isParking);
                        syntheticalSignPlanNum = NumberUtils.numAddcalculate(syntheticalSignPlanNum,monthVo.getSyntheticalSignPlanNum(),isParking);
                        syntheticalSignPlanArea = String.valueOf(new BigDecimal(syntheticalSignPlanArea).add(new BigDecimal(monthVo.getSyntheticalSignPlanArea())));
                        syntheticalSignPlanPrice = NumberUtils.priceAddcalculate(syntheticalSignPlanPrice,monthVo.getSyntheticalSignPlanPrice(),isParking);
                        syntheticalSignActualNum = NumberUtils.numAddcalculate(syntheticalSignActualNum,monthVo.getSyntheticalSignActualNum(),isParking);
                        syntheticalSignActualArea = String.valueOf(new BigDecimal(syntheticalSignActualArea).add(new BigDecimal(monthVo.getSyntheticalSignActualArea())));
                        syntheticalSignActualPrice = NumberUtils.priceAddcalculate(syntheticalSignActualPrice,monthVo.getSyntheticalSignActualPrice(),isParking);
                    }
                }
            }else {
                for (OverallSignPlanVo vo :vos) {
                    if (vo.getPid().equals(id)) {
                        List<MonthSignVo> collect = vo.getMonthSignVo().stream().filter(s -> s.getYearMonth().equals(yearMonth)).collect(toList());
                        if(CollectionUtils.isEmpty(collect)){
                            continue;
                        }
                        MonthSignVo monthVo = vo.getMonthSignVo().stream().filter(s -> s.getYearMonth().equals(yearMonth)).collect(toList()).get(0);

                        earlyNum = NumberUtils.numAdd(earlyNum,monthVo.getEarlyNum());
                        earlyArea = String.valueOf(new BigDecimal(earlyArea).add(new BigDecimal(monthVo.getEarlyArea())));
                        earlyPrice = NumberUtils.priceAdd(earlyPrice,monthVo.getEarlyPrice());
                        earlySignPlanNum = NumberUtils.numAdd(earlySignPlanNum,monthVo.getEarlySignPlanNum());
                        earlySignPlanArea = String.valueOf(new BigDecimal(earlySignPlanArea).add(new BigDecimal(monthVo.getEarlySignPlanArea())));
                        earlySignPlanPrice = NumberUtils.priceAdd(earlySignPlanPrice,monthVo.getEarlySignPlanPrice());
                        earlySignActualNum = NumberUtils.numAdd(earlySignActualNum,monthVo.getEarlySignActualNum());
                        earlySignActualArea = String.valueOf(new BigDecimal(earlySignActualArea).add(new BigDecimal(monthVo.getEarlySignActualArea())));
                        earlySignActualPrice = NumberUtils.priceAdd(earlySignActualPrice,monthVo.getEarlySignActualPrice());
                        supplyPlanNum = NumberUtils.numAdd(supplyPlanNum,monthVo.getSupplyPlanNum());
                        supplyPlanArea = String.valueOf(new BigDecimal(supplyPlanArea).add(new BigDecimal(monthVo.getSupplyPlanArea())));
                        supplyPlanPrice = NumberUtils.priceAdd(supplyPlanPrice,monthVo.getSupplyPlanPrice());
                        supplyActualNum = NumberUtils.numAdd(supplyActualNum,monthVo.getSupplyActualNum());
                        supplyActualArea = String.valueOf(new BigDecimal(supplyActualArea).add(new BigDecimal(monthVo.getSupplyActualArea())));
                        supplyActualPrice = NumberUtils.priceAdd(supplyActualPrice,monthVo.getSupplyActualPrice());
                        supplySignPlanNum = NumberUtils.numAdd(supplySignPlanNum,monthVo.getSupplySignPlanNum());
                        supplySignPlanArea = String.valueOf(new BigDecimal(supplySignPlanArea).add(new BigDecimal(monthVo.getSupplySignPlanArea())));
                        supplySignPlanPrice = NumberUtils.priceAdd(supplySignPlanPrice,monthVo.getSupplySignPlanPrice());
                        supplySignActualNum = NumberUtils.numAdd(supplySignActualNum,monthVo.getSupplySignActualNum());
                        supplySignActualArea = String.valueOf(new BigDecimal(supplySignActualArea).add(new BigDecimal(monthVo.getSupplySignActualArea())));
                        supplySignActualPrice = NumberUtils.priceAdd(supplySignActualPrice,monthVo.getSupplySignActualPrice());
                        syntheticalAvailableNum = NumberUtils.numAdd(syntheticalAvailableNum,monthVo.getSyntheticalAvailableNum());
                        syntheticalAvailableArea = String.valueOf(new BigDecimal(syntheticalAvailableArea).add(new BigDecimal(monthVo.getSyntheticalAvailableArea())));
                        syntheticalAvailablePrice = NumberUtils.priceAdd(syntheticalAvailablePrice,monthVo.getSyntheticalAvailablePrice());
                        syntheticalSignPlanNum = NumberUtils.numAdd(syntheticalSignPlanNum,monthVo.getSyntheticalSignPlanNum());
                        syntheticalSignPlanArea = String.valueOf(new BigDecimal(syntheticalSignPlanArea).add(new BigDecimal(monthVo.getSyntheticalSignPlanArea())));
                        syntheticalSignPlanPrice = NumberUtils.priceAdd(syntheticalSignPlanPrice,monthVo.getSyntheticalSignPlanPrice());
                        syntheticalSignActualNum = NumberUtils.numAdd(syntheticalSignActualNum,monthVo.getSyntheticalSignActualNum());
                        syntheticalSignActualArea = String.valueOf(new BigDecimal(syntheticalSignActualArea).add(new BigDecimal(monthVo.getSyntheticalSignActualArea())));
                        syntheticalSignActualPrice = NumberUtils.priceAdd(syntheticalSignActualPrice,monthVo.getSyntheticalSignActualPrice());
                    }
                }
            }
            //<月初存货>均价
            String avgEarlyPrice = NumberUtils.priceDiv(earlyPrice,earlyArea,earlyNum);
            //<月初存货>签约计划均价
            String earlyAvgSignPlanPrice = NumberUtils.priceDiv(earlySignPlanPrice,earlySignPlanArea,earlySignPlanNum);
            //<月初存货>签约实际均价
            String earlyAvgSignActualPrice = NumberUtils.priceDiv(earlySignActualPrice,earlySignActualArea,earlySignActualNum);
            //<月初存货>签约完成率
            String earlySignFillRate = NumberUtils.pricePercentage(earlySignActualPrice,earlySignPlanPrice);
            //<月初存货>实际去化率
            String earlyActualRemovalRate = NumberUtils.pricePercentage(earlySignActualPrice,earlyPrice);
            //<月初存货>计划去化率
            String earlyPlanRemovalRate = NumberUtils.pricePercentage(earlySignPlanPrice,earlyPrice);

            //<月度新供>供货计划均价
            String supplyAvgPlanPrice = NumberUtils.priceDiv(supplyPlanPrice,supplyPlanArea,supplyPlanNum);
            //<月度新供>供货实际均价
            String supplyAvgActualPrice = NumberUtils.priceDiv(supplyActualPrice,supplyActualArea,supplyActualNum);
            //<月度新供>签约计划均价
            String supplyAvgSignPlanPrice = NumberUtils.priceDiv(supplySignPlanPrice,supplySignPlanArea,supplySignPlanNum);
            //<月度新供>签约实际均价
            String supplyAvgSignActualPrice = NumberUtils.priceDiv(supplySignActualPrice,supplySignActualArea,supplySignActualNum);
            //<月度新供>供货完成率
            String supplyFillRate = NumberUtils.pricePercentage(supplyActualPrice,supplyPlanPrice);
            //<月度新供>签约完成率
            String supplySignFillRate = NumberUtils.pricePercentage(supplySignActualPrice,supplySignPlanPrice);
            //<月度新供>实际去化率
            String supplyActualRemovalRate = NumberUtils.pricePercentage(supplySignActualPrice,supplyActualPrice);
            //<月度新供>计划去化率
            String supplyPlanRemovalRate = NumberUtils.pricePercentage(supplySignPlanPrice,supplyPlanPrice);

            //<月度综合>可售均价
            String syntheticalAvailableAvgPrice = NumberUtils.priceDiv(syntheticalAvailablePrice,syntheticalAvailableArea,syntheticalAvailableNum);
            //<月度综合>签约计划均价
            String syntheticalAvgSignPlanPrice = NumberUtils.priceDiv(syntheticalSignPlanPrice,syntheticalSignPlanArea,syntheticalSignPlanNum);
            //<月度综合>签约实际均价
            String syntheticalAvgSignActualPrice = NumberUtils.priceDiv(syntheticalSignActualPrice,syntheticalSignActualArea,syntheticalSignActualNum);
            //<月度综合>签约完成率
            String syntheticalSignFillRate = NumberUtils.pricePercentage(syntheticalSignActualPrice,syntheticalSignPlanPrice);
            //<月度综合>实际去化率
            String syntheticalActualRemovalRate = NumberUtils.pricePercentage(syntheticalSignActualPrice,syntheticalAvailablePrice);
            //<月度综合>计划去化率
            String syntheticalPlanRemovalRate = NumberUtils.pricePercentage(syntheticalSignPlanPrice,syntheticalAvailablePrice);

            monthSignVo.setYearMonth(yearMonth);
            monthSignVo.setEarlyNum(String.valueOf(earlyNum));
            monthSignVo.setEarlyArea(String.valueOf(earlyArea));
            monthSignVo.setAvgEarlyPrice(String.valueOf(avgEarlyPrice));
            monthSignVo.setEarlyPrice(String.valueOf(earlyPrice));
            monthSignVo.setEarlySignPlanNum(String.valueOf(earlySignPlanNum));
            monthSignVo.setEarlySignPlanArea(String.valueOf(earlySignPlanArea));
            monthSignVo.setEarlyAvgSignPlanPrice(String.valueOf(earlyAvgSignPlanPrice));
            monthSignVo.setEarlySignPlanPrice(String.valueOf(earlySignPlanPrice));
            monthSignVo.setEarlySignActualNum(String.valueOf(earlySignActualNum));
            monthSignVo.setEarlySignActualArea(String.valueOf(earlySignActualArea));
            monthSignVo.setEarlyAvgSignActualPrice(String.valueOf(earlyAvgSignActualPrice));
            monthSignVo.setEarlySignActualPrice(String.valueOf(earlySignActualPrice));
            monthSignVo.setEarlySignFillRate(earlySignFillRate);
            monthSignVo.setEarlyActualRemovalRate(earlyActualRemovalRate);
            monthSignVo.setEarlyPlanRemovalRate(earlyPlanRemovalRate);
            monthSignVo.setSupplyPlanNum(String.valueOf(supplyPlanNum));
            monthSignVo.setSupplyPlanArea(String.valueOf(supplyPlanArea));
            monthSignVo.setSupplyAvgPlanPrice(String.valueOf(supplyAvgPlanPrice));
            monthSignVo.setSupplyPlanPrice(String.valueOf(supplyPlanPrice));
            monthSignVo.setSupplyActualNum(String.valueOf(supplyActualNum));
            monthSignVo.setSupplyActualArea(String.valueOf(supplyActualArea));
            monthSignVo.setSupplyAvgActualPrice(String.valueOf(supplyAvgActualPrice));
            monthSignVo.setSupplyActualPrice(String.valueOf(supplyActualPrice));
            monthSignVo.setSupplyFillRate(supplyFillRate);
            monthSignVo.setSupplySignPlanNum(String.valueOf(supplySignPlanNum));
            monthSignVo.setSupplySignPlanArea(String.valueOf(supplySignPlanArea));
            monthSignVo.setSupplyAvgSignPlanPrice(String.valueOf(supplyAvgSignPlanPrice));
            monthSignVo.setSupplySignPlanPrice(String.valueOf(supplySignPlanPrice));
            monthSignVo.setSupplySignActualNum(String.valueOf(supplySignActualNum));
            monthSignVo.setSupplySignActualArea(String.valueOf(supplySignActualArea));
            monthSignVo.setSupplyAvgSignActualPrice(String.valueOf(supplyAvgSignActualPrice));
            monthSignVo.setSupplySignActualPrice(String.valueOf(supplySignActualPrice));
            monthSignVo.setSupplySignFillRate(supplySignFillRate);
            monthSignVo.setSupplyActualRemovalRate(supplyActualRemovalRate);
            monthSignVo.setSupplyPlanRemovalRate(supplyPlanRemovalRate);
            monthSignVo.setSyntheticalAvailableNum(String.valueOf(syntheticalAvailableNum));
            monthSignVo.setSyntheticalAvailableArea(String.valueOf(syntheticalAvailableArea));
            monthSignVo.setSyntheticalAvailableAvgPrice(String.valueOf(syntheticalAvailableAvgPrice));
            monthSignVo.setSyntheticalAvailablePrice(String.valueOf(syntheticalAvailablePrice));
            monthSignVo.setSyntheticalSignPlanNum(String.valueOf(syntheticalSignPlanNum));
            monthSignVo.setSyntheticalSignPlanArea(String.valueOf(syntheticalSignPlanArea));
            monthSignVo.setSyntheticalAvgSignPlanPrice(String.valueOf(syntheticalAvgSignPlanPrice));
            monthSignVo.setSyntheticalSignPlanPrice(String.valueOf(syntheticalSignPlanPrice));
            monthSignVo.setSyntheticalSignActualNum(String.valueOf(syntheticalSignActualNum));
            monthSignVo.setSyntheticalSignActualArea(String.valueOf(syntheticalSignActualArea));
            monthSignVo.setSyntheticalAvgSignActualPrice(String.valueOf(syntheticalAvgSignActualPrice));
            monthSignVo.setSyntheticalSignActualPrice(String.valueOf(syntheticalSignActualPrice));
            monthSignVo.setSyntheticalSignFillRate(syntheticalSignFillRate);
            monthSignVo.setSyntheticalActualRemovalRate(syntheticalActualRemovalRate);
            monthSignVo.setSyntheticalPlanRemovalRate(syntheticalPlanRemovalRate);
            monthSignVoList.add(monthSignVo);
        }
        return monthSignVoList;
    }

    /**
     * 全盘供货与签约汇总
     * @param id
     * @param vos 被汇总数据集合
     * @param orgType 被汇总数据的层级类型
     * @return
     */
    private SupplyAndSignVo supplyAndSignSummarization(String id, List<OverallSignPlanVo> vos, String orgType) {
        SupplyAndSignVo supplyAndSignVo = new SupplyAndSignVo();

        //<全盘可售货值>套数/车位数
        String availableNum = NumberUtils.stringInitial(2,5);
        //<全盘可售货值>面积
        String availableArea = NumberUtils.stringInitial(1,4);
        //<全盘可售货值>金额
        String availablePrice = NumberUtils.stringInitial(3,4);
        //<累计已供>套数/车位数
        String supplyActualNum = NumberUtils.stringInitial(2,5);
        //<累计已供>面积
        String supplyActualArea = NumberUtils.stringInitial(1,4);
        //<累计已供>金额
        String supplyActualPrice = NumberUtils.stringInitial(3,4);
        //<累计已售>套数/车位数
        String signActualNum = NumberUtils.stringInitial(2,5);
        //<累计已售>面积
        String signActualArea = NumberUtils.stringInitial(1,4);
        //<累计已售>金额
        String signActualPrice = NumberUtils.stringInitial(3,4);

        // 被汇总的数据的层级是产品构成
        if (orgType.equals(OrgTypeEunm.ORG_PRODUCT.getKey())) {
            for (OverallSignPlanVo vo : vos) {
                if (vo.getPid().equals(id)) {
                    Integer isParking = vo.getIsParking();

                    SupplyAndSignVo supplyAndSign = vo.getSupplyAndSignVo();

                    availableNum = NumberUtils.numAddcalculate(availableNum, supplyAndSign.getAvailableNum(),isParking);
                    availableArea = String.valueOf(new BigDecimal(availableArea).add(new BigDecimal(supplyAndSign.getAvailableArea())));
                    availablePrice = NumberUtils.priceAddcalculate(availablePrice, supplyAndSign.getAvailablePrice(),isParking);
                    supplyActualNum = NumberUtils.numAddcalculate(supplyActualNum, supplyAndSign.getSupplyActualNum(),isParking);
                    supplyActualArea = String.valueOf(new BigDecimal(supplyActualArea).add(new BigDecimal(supplyAndSign.getSupplyActualArea())));
                    supplyActualPrice = NumberUtils.priceAddcalculate(supplyActualPrice, supplyAndSign.getSupplyActualPrice(),isParking);
                    signActualNum = NumberUtils.numAddcalculate(signActualNum, supplyAndSign.getSignActualNum(),isParking);
                    signActualArea = String.valueOf(new BigDecimal(signActualArea).add(new BigDecimal(supplyAndSign.getSignActualArea())));
                    signActualPrice = NumberUtils.priceAddcalculate(signActualPrice, supplyAndSign.getSignActualPrice(),isParking);
                }
            }
        } else {
            for (OverallSignPlanVo spvd : vos) {
                if (spvd.getPid().equals(id)) {
                    SupplyAndSignVo supplyAndSign = spvd.getSupplyAndSignVo();

                    availableNum = NumberUtils.numAdd(availableNum, supplyAndSign.getAvailableNum());
                    availableArea = String.valueOf(new BigDecimal(availableArea).add(new BigDecimal(supplyAndSign.getAvailableArea())));
                    availablePrice = NumberUtils.priceAdd(availablePrice, supplyAndSign.getAvailablePrice());
                    supplyActualNum = NumberUtils.numAdd(supplyActualNum, supplyAndSign.getSupplyActualNum());
                    supplyActualArea = String.valueOf(new BigDecimal(supplyActualArea).add(new BigDecimal(supplyAndSign.getSupplyActualArea())));
                    supplyActualPrice = NumberUtils.priceAdd(supplyActualPrice, supplyAndSign.getSupplyActualPrice());
                    signActualNum = NumberUtils.numAdd(signActualNum, supplyAndSign.getSignActualNum());
                    signActualArea = String.valueOf(new BigDecimal(signActualArea).add(new BigDecimal(supplyAndSign.getSignActualArea())));
                    signActualPrice = NumberUtils.priceAdd(signActualPrice, supplyAndSign.getSignActualPrice());
                }
            }
        }

        //<全盘可售货值>均价
        String availableAvgPrice = NumberUtils.priceDiv(availablePrice,availableArea,availableNum);
        //<累计已供>均价
        String supplyActualAvgPrice = NumberUtils.priceDiv(supplyActualPrice,supplyActualArea,supplyActualNum);
        //<累计已售>均价
        String signAvgActualPrice = NumberUtils.priceDiv(signActualPrice,signActualArea,signActualNum);
        //<供货完成率>%
        String supplyFillRate = NumberUtils.pricePercentage(supplyActualPrice,availablePrice);
        //<签约完成率>%
        String signFillRate = NumberUtils.pricePercentage(signActualPrice,availablePrice);
        //<实际去化率>%
        String actualRemovalRate = NumberUtils.pricePercentage(signActualPrice,supplyActualPrice);

        supplyAndSignVo.setAvailableNum(availableNum);
        supplyAndSignVo.setAvailableArea(availableArea);
        supplyAndSignVo.setAvailablePrice(availablePrice);
        supplyAndSignVo.setAvailableAvgPrice(availableAvgPrice);
        supplyAndSignVo.setSupplyActualNum(supplyActualNum);
        supplyAndSignVo.setSupplyActualArea(supplyActualArea);
        supplyAndSignVo.setSupplyActualPrice(supplyActualPrice);
        supplyAndSignVo.setSupplyActualAvgPrice(supplyActualAvgPrice);
        supplyAndSignVo.setSignActualNum(signActualNum);
        supplyAndSignVo.setSignActualArea(signActualArea);
        supplyAndSignVo.setSignActualPrice(signActualPrice);
        supplyAndSignVo.setSignAvgActualPrice(signAvgActualPrice);
        supplyAndSignVo.setSupplyFillRate(supplyFillRate);
        supplyAndSignVo.setSignFillRate(signFillRate);
        supplyAndSignVo.setActualRemovalRate(actualRemovalRate);

        return supplyAndSignVo;
    }

    /**
     * 设置考核版
     * @param versionId 版本id
     * @return
     */
    @Override
    public String settingsAssessmentVersion(String versionId) {
        //查询版本
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
        if(null == signPlanVersion){
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }
        if(!signPlanVersion.getVersionState().equals(VersionStatusEnum.SUCCESSAPPROVED.getKey())){
            throw new MsgException(CodeEnum.IS_ASSESSMENTVERSION_VERSION_STATUS_ERROR);
        }
        //查询来源供货
        String sourceSupplyId = signPlanVersion.getSourceSupplyId();
        SupplyPlanVersion supplyPlanVersion = supplyPlanVersionMapper.selectById(sourceSupplyId);
        Integer isAssessmentVersion = supplyPlanVersion.getIsAssessmentVersion();
        if(isAssessmentVersion == null || isAssessmentVersion !=WhetherEnum.YES.getKey()){
            throw new MsgException(CodeEnum.IS_ASSESSMENTVERSION_ERROR);
        }
        //清除考核版
        signPlanVersionMapper.updateIsAssessmentVersionByProjectfId(signPlanVersion.getProjectfId(),IsDeleteEnum.NO.getKey());
        //设置考核版
        signPlanVersion.setIsAssessmentVersion(WhetherEnum.YES.getKey());
        signPlanVersionMapper.updateById(signPlanVersion);
        saveLog(LogEventEnum.PRESERVATION,"签约计划","设置考核版",signPlanVersion.toString());
        return "设置成功";
    }

    /**
     * 供货考核版修改,更新签约考核版
     * @param versionId 供货版本id
     * @param projectfId 分期id
     */
    @Override
    public void updateAssessmentVersion(String versionId,String projectfId){
        saveLog(LogEventEnum.REFRESH,"签约计划","更新签约考核版","versionId:"+versionId+",projectfId:"+projectfId);
        //清除考核版
        signPlanVersionMapper.updateIsAssessmentVersionByProjectfId(projectfId,IsDeleteEnum.NO.getKey());
        //当前分期下不存在考核版供货计划版本
        if(versionId == null){
            return;
        }
        //查询基于供货考核版生成的第一版签约计划
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectBySourceSupplyId(versionId,IsDeleteEnum.NO.getKey());
        if(null == signPlanVersion){
            //当前不存在基于供货考核版生成的第一版签约计划
            return;
        }
        //设置考核版
        signPlanVersion.setIsAssessmentVersion(WhetherEnum.YES.getKey());
        signPlanVersionMapper.updateById(signPlanVersion);
        saveLog(LogEventEnum.PRESERVATION,"签约计划","供货考核版修改,更新签约考核版",signPlanVersion.toString());
    }

    /**
     * 全盘签约计划报表精度修改(套数、套均面积、面积保留两位小数,均价、金额万元取整)
     * @param voList 全盘签约计划报表数据
     */
    @Override
    public void updateOverallSignPlanVoAccuracy(List<OverallSignPlanVo> voList) {
        for (OverallSignPlanVo vo:voList){
            SupplyAndSignVo supplyAndSignVo = vo.getSupplyAndSignVo();
            supplyAndSignVo.setAvailableNum(NumberUtils.updateBigAccuracy(supplyAndSignVo.getAvailableNum(),2));
            supplyAndSignVo.setAvailableArea(NumberUtils.updateBigAccuracy(supplyAndSignVo.getAvailableArea(),2));
            supplyAndSignVo.setAvailableAvgPrice(NumberUtils.updateBigAccuracy(supplyAndSignVo.getAvailableAvgPrice(),0));
            supplyAndSignVo.setAvailablePrice(NumberUtils.priceToWanAndMicrometer(supplyAndSignVo.getAvailablePrice()));
            supplyAndSignVo.setSupplyActualNum(NumberUtils.updateBigAccuracy(supplyAndSignVo.getSupplyActualNum(),2));
            supplyAndSignVo.setSupplyActualArea(NumberUtils.updateBigAccuracy(supplyAndSignVo.getSupplyActualArea(),2));
            supplyAndSignVo.setSupplyActualAvgPrice(NumberUtils.updateBigAccuracy(supplyAndSignVo.getSupplyActualAvgPrice(),0));
            supplyAndSignVo.setSupplyActualPrice(NumberUtils.priceToWanAndMicrometer(supplyAndSignVo.getSupplyActualPrice()));
            supplyAndSignVo.setSignActualNum(NumberUtils.updateBigAccuracy(supplyAndSignVo.getSignActualNum(),2));
            supplyAndSignVo.setSignActualArea(NumberUtils.updateBigAccuracy(supplyAndSignVo.getSignActualArea(),2));
            supplyAndSignVo.setSignAvgActualPrice(NumberUtils.updateBigAccuracy(supplyAndSignVo.getSignAvgActualPrice(),0));
            supplyAndSignVo.setSignActualPrice(NumberUtils.priceToWanAndMicrometer(supplyAndSignVo.getSignActualPrice()));
            List<MonthSignVo> monthSignVoList = vo.getMonthSignVo();
            for (MonthSignVo monthSignVo:monthSignVoList){
                monthSignVo.setEarlyNum(NumberUtils.updateBigAccuracy(monthSignVo.getEarlyNum(),2));
                monthSignVo.setEarlyArea(NumberUtils.updateBigAccuracy(monthSignVo.getEarlyArea(),2));
                monthSignVo.setAvgEarlyPrice(NumberUtils.updateBigAccuracy(monthSignVo.getAvgEarlyPrice(),0));
                monthSignVo.setEarlyPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getEarlyPrice()));
                monthSignVo.setEarlySignPlanNum(NumberUtils.updateBigAccuracy(monthSignVo.getEarlySignPlanNum(),2));
                monthSignVo.setEarlySignPlanArea(NumberUtils.updateBigAccuracy(monthSignVo.getEarlySignPlanArea(),2));
                monthSignVo.setEarlyAvgSignPlanPrice(NumberUtils.updateBigAccuracy(monthSignVo.getEarlyAvgSignPlanPrice(),0));
                monthSignVo.setEarlySignPlanPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getEarlySignPlanPrice()));
                monthSignVo.setEarlySignActualNum(NumberUtils.updateBigAccuracy(monthSignVo.getEarlySignActualNum(),2));
                monthSignVo.setEarlySignActualArea(NumberUtils.updateBigAccuracy(monthSignVo.getEarlySignActualArea(),2));
                monthSignVo.setEarlyAvgSignActualPrice(NumberUtils.updateBigAccuracy(monthSignVo.getEarlyAvgSignActualPrice(),0));
                monthSignVo.setEarlySignActualPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getEarlySignActualPrice()));
                monthSignVo.setSupplyPlanNum(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyPlanNum(),2));
                monthSignVo.setSupplyPlanArea(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyPlanArea(),2));
                monthSignVo.setSupplyAvgPlanPrice(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyAvgPlanPrice(),0));
                monthSignVo.setSupplyPlanPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getSupplyPlanPrice()));
                monthSignVo.setSupplyActualNum(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyActualNum(),2));
                monthSignVo.setSupplyActualArea(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyActualArea(),2));
                monthSignVo.setSupplyAvgActualPrice(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyAvgActualPrice(),0));
                monthSignVo.setSupplyActualPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getSupplyActualPrice()));
                monthSignVo.setSupplySignPlanNum(NumberUtils.updateBigAccuracy(monthSignVo.getSupplySignPlanNum(),2));
                monthSignVo.setSupplySignPlanArea(NumberUtils.updateBigAccuracy(monthSignVo.getSupplySignPlanArea(),2));
                monthSignVo.setSupplyAvgSignPlanPrice(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyAvgSignPlanPrice(),0));
                monthSignVo.setSupplySignPlanPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getSupplySignPlanPrice()));
                monthSignVo.setSupplySignActualNum(NumberUtils.updateBigAccuracy(monthSignVo.getSupplySignActualNum(),2));
                monthSignVo.setSupplySignActualArea(NumberUtils.updateBigAccuracy(monthSignVo.getSupplySignActualArea(),2));
                monthSignVo.setSupplyAvgSignActualPrice(NumberUtils.updateBigAccuracy(monthSignVo.getSupplyAvgSignActualPrice(),0));
                monthSignVo.setSupplySignActualPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getSupplySignActualPrice()));
                monthSignVo.setSyntheticalAvailableNum(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalAvailableNum(),2));
                monthSignVo.setSyntheticalAvailableArea(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalAvailableArea(),2));
                monthSignVo.setSyntheticalAvailableAvgPrice(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalAvailableAvgPrice(),0));
                monthSignVo.setSyntheticalAvailablePrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getSyntheticalAvailablePrice()));
                monthSignVo.setSyntheticalSignPlanNum(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalSignPlanNum(),2));
                monthSignVo.setSyntheticalSignPlanArea(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalSignPlanArea(),2));
                monthSignVo.setSyntheticalAvgSignPlanPrice(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalAvgSignPlanPrice(),0));
                monthSignVo.setSyntheticalSignPlanPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getSyntheticalSignPlanPrice()));
                monthSignVo.setSyntheticalSignActualNum(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalSignActualNum(),2));
                monthSignVo.setSyntheticalSignActualArea(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalSignActualArea(),2));
                monthSignVo.setSyntheticalAvgSignActualPrice(NumberUtils.updateBigAccuracy(monthSignVo.getSyntheticalAvgSignActualPrice(),0));
                monthSignVo.setSyntheticalSignActualPrice(NumberUtils.priceToWanAndMicrometer(monthSignVo.getSyntheticalSignActualPrice()));
            }

            List<YearSignVo> yearSignVoList = vo.getYearSignVo();
            for (YearSignVo yearSignVo:yearSignVoList){
                yearSignVo.setEarlyNum(NumberUtils.updateBigAccuracy(yearSignVo.getEarlyNum(),2));
                yearSignVo.setEarlyArea(NumberUtils.updateBigAccuracy(yearSignVo.getEarlyArea(),2));
                yearSignVo.setEarlyAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getEarlyAvgPrice(),0));
                yearSignVo.setEarlyPrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getEarlyPrice()));
                yearSignVo.setEarlySignActualNum(NumberUtils.updateBigAccuracy(yearSignVo.getEarlySignActualNum(),2));
                yearSignVo.setEarlySignActualArea(NumberUtils.updateBigAccuracy(yearSignVo.getEarlySignActualArea(),2));
                yearSignVo.setEarlySignActualAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getEarlySignActualAvgPrice(),0));
                yearSignVo.setEarlySignActualPrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getEarlySignActualPrice()));
                yearSignVo.setSupplyPlanNum(NumberUtils.updateBigAccuracy(yearSignVo.getSupplyPlanNum(),2));
                yearSignVo.setSupplyPlanArea(NumberUtils.updateBigAccuracy(yearSignVo.getSupplyPlanArea(),2));
                yearSignVo.setSupplyPlanAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getSupplyPlanAvgPrice(),0));
                yearSignVo.setSupplyPlanPrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getSupplyPlanPrice()));
                yearSignVo.setSupplyActualNum(NumberUtils.updateBigAccuracy(yearSignVo.getSupplyActualNum(),2));
                yearSignVo.setSupplyActualArea(NumberUtils.updateBigAccuracy(yearSignVo.getSupplyActualArea(),2));
                yearSignVo.setSupplyActualAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getSupplyActualAvgPrice(),0));
                yearSignVo.setSupplyActualPrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getSupplyActualPrice()));
                yearSignVo.setSupplySignActualNum(NumberUtils.updateBigAccuracy(yearSignVo.getSupplySignActualNum(),2));
                yearSignVo.setSupplySignActualArea(NumberUtils.updateBigAccuracy(yearSignVo.getSupplySignActualArea(),2));
                yearSignVo.setSupplySignActualAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getSupplySignActualAvgPrice(),0));
                yearSignVo.setSupplySignActualPrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getSupplySignActualPrice()));
                yearSignVo.setSyntheticalAvailableNum(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalAvailableNum(),2));
                yearSignVo.setSyntheticalAvailableArea(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalAvailableArea(),2));
                yearSignVo.setSyntheticalAvailableAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalAvailableAvgPrice(),0));
                yearSignVo.setSyntheticalAvailablePrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getSyntheticalAvailablePrice()));
                yearSignVo.setYearSigningTargetCount(NumberUtils.updateBigAccuracy(yearSignVo.getYearSigningTargetCount(),2));
                yearSignVo.setYearSigningTargetArea(NumberUtils.updateBigAccuracy(yearSignVo.getYearSigningTargetArea(),2));
                yearSignVo.setYearSigningTargetAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getYearSigningTargetAvgPrice(),0));
                yearSignVo.setYearSigningTargetAmount(NumberUtils.priceToWanAndMicrometer(yearSignVo.getYearSigningTargetAmount()));
                yearSignVo.setSyntheticalSignPlanNum(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalSignPlanNum(),2));
                yearSignVo.setSyntheticalSignPlanArea(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalSignPlanArea(),2));
                yearSignVo.setSyntheticalSignPlanAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalSignPlanAvgPrice(),0));
                yearSignVo.setSyntheticalSignPlanPrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getSyntheticalSignPlanPrice()));
                yearSignVo.setSyntheticalSignActualNum(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalSignActualNum(),2));
                yearSignVo.setSyntheticalSignActualArea(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalSignActualArea(),2));
                yearSignVo.setSyntheticalSignActualAvgPrice(NumberUtils.updateBigAccuracy(yearSignVo.getSyntheticalSignActualAvgPrice(),0));
                yearSignVo.setSyntheticalSignActualPrice(NumberUtils.priceToWanAndMicrometer(yearSignVo.getSyntheticalSignActualPrice()));
            }
        }
    }

    /**
     * 获取版本列表中最早供货时间与最晚签约时间
     * @param versionIds
     * @return
     */
    @Override
    public StartAndEndTimeBO getStartAndEndTime(List<String> versionIds) {
        StartAndEndTimeBO startAndEndTimeBO = new StartAndEndTimeBO();
        List<SignPlanVersionDetail> spvdList = signPlanVersionDetailMapper.selectByVersionIds(versionIds, WhetherEnum.NO.getKey());
        spvdList.sort(Comparator.comparing(SignPlanVersionDetail::getSignPlanTime));
        if(CollectionUtils.isNotEmpty(spvdList)) {
        	LocalDateTime earliestSupplyDate = spvdList.get(0).getSignPlanTime();
            LocalDateTime latestSignDate = spvdList.get(spvdList.size() - 1).getSignPlanTime();
            startAndEndTimeBO.setStartDate(earliestSupplyDate);
            startAndEndTimeBO.setEndDate(latestSignDate);
        }
        return startAndEndTimeBO;
    }

    /**
     * 根据id查询签约版本
     * @param versionId
     * @return
     */
    @Override
    public SignPlanVersion selectById(String versionId) {
        return signPlanVersionMapper.selectById(versionId);
    }


    /**
     * 计算全盘产品构成数据
     *
     * @param projectfId       分期id
     * @param productList      产品构成供货详情
     * @param sourceSupplyId   来源供货版本id
     * @param startAndEndTimeBO 开始日期与结束时间
     * @param affiliationMonth 版本所属时间
     * @param groupId          组团id
     * @return
     */
    private List<OverallSignPlanVo> calculateOverallProduct(List<SignPlanVersionSupplyDetail> productList, String sourceSupplyId,StartAndEndTimeBO startAndEndTimeBO, LocalDateTime affiliationMonth, String groupId) {
        List<OverallSignPlanVo> productVoList = new ArrayList<>();

        for (SignPlanVersionSupplyDetail product : productList) {
            // 初始化签约供货详情查询条件
            SignProductCodeBO signSupplyBO = new SignProductCodeBO();
            signSupplyBO.setVersionId(product.getVersionId());
            signSupplyBO.setPid(product.getPid());
            signSupplyBO.setProductCode(product.getOrgCode());
            signSupplyBO.setBusinessType(product.getBusinessType());
            signSupplyBO.setFreeType(product.getFreeType());
            signSupplyBO.setProType(product.getProType());
            signSupplyBO.setIsDel(IsDeleteEnum.NO.getKey());
            // 初始化签约详情查询条件
            SignProductCodeBO signDetailBO = new SignProductCodeBO();
            signDetailBO.setVersionId(product.getVersionId());
            signDetailBO.setPid(groupId);
            signDetailBO.setProductCode(product.getOrgCode());
            signDetailBO.setBusinessType(product.getBusinessType());
            signDetailBO.setFreeType(product.getFreeType());
            signDetailBO.setProType(product.getProType());
            signDetailBO.setIsDel(IsDeleteEnum.NO.getKey());

            // 是否车位楼栋
            Integer isParking = product.getIsParking();

            OverallSignPlanVo vo = new OverallSignPlanVo();
            vo.setId(product.getSupplyPlanId());
            vo.setPid(product.getPid());
            vo.setProductCode(product.getOrgCode());
            vo.setProductName(product.getOrgName());
            vo.setBusinessTypeName(product.getBusinessTypeName());
            vo.setFreeTypeName(product.getFreeTypeName());
            vo.setProTypeName(product.getProTypeName());
            vo.setProProductCode(product.getProProductCode());
            vo.setProProductName(product.getProProductType());
            vo.setIsParking(isParking);


            // 查询组团下产品构成类型供货详情列表
            List<SignPlanVersionSupplyDetail> productSupply = signPlanVersionSupplyDetailMapper.selectSupplyPlanDetail(signSupplyBO);
            if (null == productSupply || productSupply.isEmpty()) {
                continue;
            }
            // 取签约供货详情,有实际取实际，无实际取计划
            List<SignSupplyDetailBO> ssdBOList = switchSignSupplyDetailBO(productSupply);
            //查询产品构成全部签约详情数据
            List<SignPlanVersionDetail> productSignList = signPlanVersionDetailMapper.selectSignDetail(signDetailBO);

            //计算产品构成全盘供货与签约
            SupplyAndSignVo supplyAndSignVo = getSupplyAndSignVo(product, productSignList, isParking);
            //计算产品构成月度全盘签约
            List<MonthSignVo> monthSignVoList = getMonthSignVoList(productSupply, productSignList,startAndEndTimeBO, isParking);
            //计算产品构成年度全盘签约
            List<YearSignVo> yearSignVoList = getYearSignVoList(monthSignVoList,ssdBOList,productSignList,startAndEndTimeBO,affiliationMonth,isParking);

            vo.setSupplyAndSignVo(supplyAndSignVo);
            vo.setMonthSignVo(monthSignVoList);
            vo.setYearSignVo(yearSignVoList);
            productVoList.add(vo);
        }
        return productVoList;
    }


    /**
     * 计算产品构成年度全盘签约
     * @param monthSignVoList 产品构成月度全盘签约
     * @param ssdBOList 供货详情列表
     * @param productSignList 产品构成签约数据
     * @param startAndEndTimeBO 开始时间与结束时间
     * @param affiliationMonth 版本所属时间
     * @param isParking 是否车位楼栋
     * @return
     */
    private List<YearSignVo> getYearSignVoList(List<MonthSignVo> monthSignVoList,List<SignSupplyDetailBO> ssdBOList, List<SignPlanVersionDetail> productSignList,StartAndEndTimeBO startAndEndTimeBO,LocalDateTime affiliationMonth,int isParking) {
        List<YearSignVo> yearSignVoList = new ArrayList<>();

        //开始时间
        LocalDateTime startDate = startAndEndTimeBO.getStartDate();
        //结束时间
        LocalDateTime endDate = startAndEndTimeBO.getEndDate();
        List<String> yearList = new ArrayList<>();
        int earliestyear = startDate.getYear();
        int latestyear = endDate.getYear();
        for (int year = earliestyear; year <= latestyear; year++) {
            yearList.add(String.valueOf(year));
        }
        for (String year : yearList){
            YearSignVo vo = new YearSignVo();
            String yearMonth = year+"-01";
            LocalDateTime time = LocalDateTimeUtils.getEarliestMonthDate(year);

            ///////////////////////////////年度签约目标//////////////////////////////////
            // 当年签约数据
            List<SignPlanVersionDetail> yearDetailList = productSignList.stream().filter(s -> s.getSignPlanTime().toString().contains(year)).collect(Collectors.toList());
            // 年度签约目标套数
            BigDecimal yearSigningTargetCount = BigDecimal.ZERO;
            // 年度签约目标面积
            BigDecimal yearSigningTargetArea = BigDecimal.ZERO;
            // 年度签约目标均价
            BigDecimal yearSigningTargetAvgPrice = BigDecimal.ZERO;
            // 年度签约目标金额
            BigDecimal yearSigningTargetAmount = BigDecimal.ZERO;
            for (SignPlanVersionDetail detail : yearDetailList) {
                // 当前年月是否早于编制时间，若早于取签约计划详情数据，否则取签约实际详情数据
                if (LocalDateTimeUtils.compareDate(detail.getSignPlanTime(), affiliationMonth, 0)) {
                    yearSigningTargetCount = yearSigningTargetCount.add(BigDecimal.valueOf(detail.getEarlySignRoomCountActual().longValue() + detail.getSupplySignRoomCountActual().longValue()));
                    yearSigningTargetArea = yearSigningTargetArea.add(detail.getEarlySignRoomAreaActual().add(detail.getSupplySignRoomAreaActual()));
                    yearSigningTargetAmount = yearSigningTargetAmount.add(detail.getEarlySignSumPriceActual().add(detail.getSupplySignSumPriceActual()));
                } else {
                    yearSigningTargetCount = yearSigningTargetCount.add(detail.getEarlySignRoomCount().add(detail.getSupplySignRoomCount()));
                    yearSigningTargetArea = yearSigningTargetArea.add(detail.getEarlySignRoomArea().add(detail.getSupplySignRoomArea()));
                    yearSigningTargetAmount = yearSigningTargetAmount.add(detail.getEarlySignSumPrice().add(detail.getSupplySignSumPrice()));
                }
            }
            if(isParking == WhetherEnum.YES.getKey()){
                yearSigningTargetAvgPrice = BigDecimalUtils.divBigDecimal(yearSigningTargetAmount,yearSigningTargetCount,4);
            }else {
                yearSigningTargetAvgPrice = BigDecimalUtils.divBigDecimal(yearSigningTargetAmount,yearSigningTargetArea,4);
            }

            ///////////////////////////////年初存货//////////////////////////////////
            //年初存货签约详情
            SignProductDetailBO yearEarlySignDetail = getYearEalySignSumPriceActualByTime(productSignList, time,isParking);
            List<MonthSignVo> collect = monthSignVoList.stream().filter(s -> s.getYearMonth().contains(yearMonth)).collect(Collectors.toList());

            //<年初存货>套数/车位数
            BigDecimal earlyNum;
            //<年初存货>面积
            BigDecimal earlyArea;
            //<年初存货>均价
            BigDecimal earlyAvgPrice;
            //<年初存货>金额
            BigDecimal earlyPrice;
            //<年初存货>签约实际 套数/车位数
            Integer earlySignActualNum;
            //<年初存货>签约实际面积
            BigDecimal earlySignActualArea;
            //<年初存货>签约实际均价
            BigDecimal earlySignActualAvgPrice;
            //<年初存货>签约实际金额
            BigDecimal earlySignActualPrice;
            //<年初存货>实际去化率
            String earlyActualRemovalRate;
            if(CollectionUtils.isEmpty(collect)){
                earlyNum = BigDecimal.ZERO;
                earlyArea = BigDecimal.ZERO;
                earlyAvgPrice = BigDecimal.ZERO;
                earlyPrice = BigDecimal.ZERO;
                earlySignActualNum = 0;
                earlySignActualArea = BigDecimal.ZERO;
                earlySignActualAvgPrice = BigDecimal.ZERO;
                earlySignActualPrice = BigDecimal.ZERO;
                earlyActualRemovalRate = BigDecimalUtils.calculatedPercentage(earlySignActualPrice,earlyPrice);
            }else {
                MonthSignVo earlyVo = collect.get(0);
                earlyNum = new BigDecimal(earlyVo.getEarlyNum());
                earlyArea = new BigDecimal(earlyVo.getEarlyArea());
                earlyAvgPrice = new BigDecimal(earlyVo.getAvgEarlyPrice());
                earlyPrice = new BigDecimal(earlyVo.getEarlyPrice());
                earlySignActualNum = yearEarlySignDetail.getRoomCount();
                earlySignActualArea = yearEarlySignDetail.getRoomArea();
                earlySignActualAvgPrice = yearEarlySignDetail.getAvgPrice();
                earlySignActualPrice = yearEarlySignDetail.getSumPrice();
                earlyActualRemovalRate = BigDecimalUtils.calculatedPercentage(earlySignActualPrice,earlyPrice);
            }
            ///////////////////////////////年度新供//////////////////////////////////
            //年度新供
            List<MonthSignVo> supplyVoList = monthSignVoList.stream().filter(s -> s.getYearMonth().contains(year)).collect(Collectors.toList());
            //<年度新供>供货计划套数/车位数
            Integer supplyPlanNum = supplyVoList.stream().mapToInt(s -> Integer.valueOf(s.getSupplyPlanNum())).sum();
            //<年度新供>供货计划面积
            BigDecimal supplyPlanArea = supplyVoList.stream().map(s -> new BigDecimal(s.getSupplyPlanArea())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度新供>供货计划金额
            BigDecimal supplyPlanPrice = supplyVoList.stream().map(s -> new BigDecimal(s.getSupplyPlanPrice())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度新供>供货计划均价
            BigDecimal supplyPlanAvgPrice;
            if(isParking == WhetherEnum.YES.getKey()){
                supplyPlanAvgPrice = BigDecimalUtils.divInteger(supplyPlanPrice,supplyPlanNum,4);
            }else {
                supplyPlanAvgPrice = BigDecimalUtils.divBigDecimal(supplyPlanPrice,supplyPlanArea,4);
            }
            //<年度新供>供货实际套数/车位数
            Integer supplyActualNum = supplyVoList.stream().mapToInt(s -> Integer.valueOf(s.getSupplyActualNum())).sum();
            //<年度新供>供货实际面积
            BigDecimal supplyActualArea = supplyVoList.stream().map(s -> new BigDecimal(s.getSupplyActualArea())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度新供>供货实际金额
            BigDecimal supplyActualPrice = supplyVoList.stream().map(s -> new BigDecimal(s.getSupplyActualPrice())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度新供>供货实际均价
            BigDecimal supplyActualAvgPrice;
            if(isParking == WhetherEnum.YES.getKey()){
                supplyActualAvgPrice = BigDecimalUtils.divInteger(supplyActualPrice,supplyActualNum,4);
            }else {
                supplyActualAvgPrice = BigDecimalUtils.divBigDecimal(supplyActualPrice,supplyActualArea,4);
            }
            //<年度新供>供货完成率
            String supplyFillRate = BigDecimalUtils.calculatedPercentage(supplyActualPrice,supplyPlanPrice);
            //<年度新供>签约实际 套数/车位数
            Integer supplySignActualNum = supplyVoList.stream().mapToInt(s -> Integer.valueOf(s.getSupplySignActualNum())).sum();
            //<年度新供>签约实际面积
            BigDecimal supplySignActualArea = supplyVoList.stream().map(s -> new BigDecimal(s.getSupplySignActualArea())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度新供>签约实际金额
            BigDecimal supplySignActualPrice = supplyVoList.stream().map(s -> new BigDecimal(s.getSupplySignActualPrice())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度新供>签约实际均价
            BigDecimal supplySignActualAvgPrice;
            if(isParking == WhetherEnum.YES.getKey()){
                supplySignActualAvgPrice = BigDecimalUtils.divInteger(supplySignActualPrice,supplySignActualNum,4);
            }else {
                supplySignActualAvgPrice = BigDecimalUtils.divBigDecimal(supplySignActualPrice,supplySignActualArea,4);
            }
            //<年度新供>实际去化率
            String supplyActualRemovalRate = BigDecimalUtils.calculatedPercentage(supplySignActualPrice,supplyActualPrice);

            ///////////////////////////////////年度综合////////////////////////////////////
            // 年度新供供货详情(有实际取月度实际供货金额，无实际取计划金额)
            SignProductDetailBO yearSupplyDetail = getSupplyDetail(ssdBOList, time, isParking, 0);

            //<年度综合>可售 套数/车位数
            BigDecimal syntheticalAvailableNum = earlyNum.add(new BigDecimal(yearSupplyDetail.getRoomCount()));
            //<年度综合>可售面积
            BigDecimal syntheticalAvailableArea = earlyArea.add(yearSupplyDetail.getRoomArea());
            //<年度综合>可售金额
            BigDecimal syntheticalAvailablePrice = earlyPrice.add(yearSupplyDetail.getSumPrice());
            //<年度综合>可售均价
            BigDecimal syntheticalAvailableAvgPrice;
            if(isParking == WhetherEnum.YES.getKey()){
                syntheticalAvailableAvgPrice = BigDecimalUtils.divBigDecimal(syntheticalAvailablePrice,syntheticalAvailableNum,4);
            }else {
                syntheticalAvailableAvgPrice = BigDecimalUtils.divBigDecimal(syntheticalAvailablePrice,syntheticalAvailableArea,4);
            }
            //<年度综合>签约计划 套数/车位数
            BigDecimal syntheticalSignPlanNum = supplyVoList.stream().map(s -> new BigDecimal(s.getSyntheticalSignPlanNum())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度综合>签约计划面积
            BigDecimal syntheticalSignPlanArea = supplyVoList.stream().map(s -> new BigDecimal(s.getSyntheticalSignPlanArea())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度综合>签约计划金额
            BigDecimal syntheticalSignPlanPrice = supplyVoList.stream().map(s -> new BigDecimal(s.getSyntheticalSignPlanPrice())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度综合>签约计划均价
            BigDecimal syntheticalSignPlanAvgPrice;
            if(isParking == WhetherEnum.YES.getKey()){
                syntheticalSignPlanAvgPrice = BigDecimalUtils.divBigDecimal(syntheticalSignPlanPrice,syntheticalSignPlanNum,4);
            }else {
                syntheticalSignPlanAvgPrice = BigDecimalUtils.divBigDecimal(syntheticalSignPlanPrice,syntheticalSignPlanArea,4);
            }
            //<年度综合>签约实际 套数/车位数
            Integer syntheticalSignActualNum = supplyVoList.stream().mapToInt(s -> Integer.valueOf(s.getSyntheticalSignActualNum())).sum();
            //<年度综合>签约实际面积
            BigDecimal syntheticalSignActualArea = supplyVoList.stream().map(s -> new BigDecimal(s.getSyntheticalSignActualArea())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度综合>签约实际金额
            BigDecimal syntheticalSignActualPrice = supplyVoList.stream().map(s -> new BigDecimal(s.getSyntheticalSignActualPrice())).reduce(BigDecimal.ZERO,BigDecimal::add);
            //<年度综合>签约实际均价
            BigDecimal syntheticalSignActualAvgPrice;
            if(isParking == WhetherEnum.YES.getKey()){
                syntheticalSignActualAvgPrice = BigDecimalUtils.divInteger(syntheticalSignActualPrice,syntheticalSignActualNum,4);
            }else {
                syntheticalSignActualAvgPrice = BigDecimalUtils.divBigDecimal(syntheticalSignActualPrice,syntheticalSignActualArea,4);
            }
            //<年度综合>签约完成率
            String syntheticalSignFillRate = BigDecimalUtils.calculatedPercentage(syntheticalSignActualPrice,syntheticalSignPlanPrice);
            //<年度综合>实际去化率
            String syntheticalActualRemovalRate = BigDecimalUtils.calculatedPercentage(syntheticalSignActualPrice,syntheticalAvailablePrice);
            //<年度综合>计划去化率
            String syntheticalPlanRemovalRate = BigDecimalUtils.calculatedPercentage(syntheticalSignPlanPrice,syntheticalAvailablePrice);

            vo.setYear(year);
            vo.setEarlyNum(String.valueOf(earlyNum));
            vo.setEarlyArea(String.valueOf(earlyArea));
            vo.setEarlyAvgPrice(String.valueOf(earlyAvgPrice));
            vo.setEarlyPrice(String.valueOf(earlyPrice));
            vo.setEarlySignActualNum(String.valueOf(earlySignActualNum));
            vo.setEarlySignActualArea(String.valueOf(earlySignActualArea));
            vo.setEarlySignActualAvgPrice(String.valueOf(earlySignActualAvgPrice));
            vo.setEarlySignActualPrice(String.valueOf(earlySignActualPrice));
            vo.setEarlyActualRemovalRate(String.valueOf(earlyActualRemovalRate));
            vo.setSupplyPlanNum(String.valueOf(supplyPlanNum));
            vo.setSupplyPlanArea(String.valueOf(supplyPlanArea));
            vo.setSupplyPlanAvgPrice(String.valueOf(supplyPlanAvgPrice));
            vo.setSupplyPlanPrice(String.valueOf(supplyPlanPrice));
            vo.setSupplyActualNum(String.valueOf(supplyActualNum));
            vo.setSupplyActualArea(String.valueOf(supplyActualArea));
            vo.setSupplyActualAvgPrice(String.valueOf(supplyActualAvgPrice));
            vo.setSupplyActualPrice(String.valueOf(supplyActualPrice));
            vo.setSupplyFillRate(String.valueOf(supplyFillRate));
            vo.setSupplySignActualNum(String.valueOf(supplySignActualNum));
            vo.setSupplySignActualArea(String.valueOf(supplySignActualArea));
            vo.setSupplySignActualAvgPrice(String.valueOf(supplySignActualAvgPrice));
            vo.setSupplySignActualPrice(String.valueOf(supplySignActualPrice));
            vo.setSupplyActualRemovalRate(String.valueOf(supplyActualRemovalRate));
            vo.setSyntheticalAvailableNum(String.valueOf(syntheticalAvailableNum));
            vo.setSyntheticalAvailableArea(String.valueOf(syntheticalAvailableArea));
            vo.setSyntheticalAvailableAvgPrice(String.valueOf(syntheticalAvailableAvgPrice));
            vo.setSyntheticalAvailablePrice(String.valueOf(syntheticalAvailablePrice));
            vo.setYearSigningTargetCount(String.valueOf(yearSigningTargetCount));
            vo.setYearSigningTargetArea(String.valueOf(yearSigningTargetArea));
            vo.setYearSigningTargetAvgPrice(String.valueOf(yearSigningTargetAvgPrice));
            vo.setYearSigningTargetAmount(String.valueOf(yearSigningTargetAmount));
            vo.setSyntheticalSignPlanNum(String.valueOf(syntheticalSignPlanNum));
            vo.setSyntheticalSignPlanArea(String.valueOf(syntheticalSignPlanArea));
            vo.setSyntheticalSignPlanAvgPrice(String.valueOf(syntheticalSignPlanAvgPrice));
            vo.setSyntheticalSignPlanPrice(String.valueOf(syntheticalSignPlanPrice));
            vo.setSyntheticalSignActualNum(String.valueOf(syntheticalSignActualNum));
            vo.setSyntheticalSignActualArea(String.valueOf(syntheticalSignActualArea));
            vo.setSyntheticalSignActualAvgPrice(String.valueOf(syntheticalSignActualAvgPrice));
            vo.setSyntheticalSignActualPrice(String.valueOf(syntheticalSignActualPrice));
            vo.setSyntheticalSignFillRate(String.valueOf(syntheticalSignFillRate));
            vo.setSyntheticalActualRemovalRate(String.valueOf(syntheticalActualRemovalRate));
            vo.setSyntheticalPlanRemovalRate(String.valueOf(syntheticalPlanRemovalRate));
            yearSignVoList.add(vo);
        }
        return yearSignVoList;
    }


    /**
     * 计算产品构成月度全盘签约
     *
     * @param productSupplyList 产品构成供货详情列表
     * @param productSignList 产品构成供货签约列表
     * @param startAndEndTimeBO 起始时间与结束时间
     * @param isParking 是否车位
     * @return
     */
    private List<MonthSignVo> getMonthSignVoList(List<SignPlanVersionSupplyDetail> productSupplyList, List<SignPlanVersionDetail> productSignList,StartAndEndTimeBO startAndEndTimeBO, Integer isParking) {
        List<MonthSignVo> monthSignVoList = new ArrayList<>();

        //开始时间
        LocalDateTime startDate = startAndEndTimeBO.getStartDate();
        //结束时间
        LocalDateTime endDate = startAndEndTimeBO.getEndDate();
        for (LocalDateTime time = startDate; LocalDateTimeUtils.compareDate(time, endDate,
                1); time = LocalDateTimeUtils.addDate(time)) {
            MonthSignVo monthSignVo = new MonthSignVo();

            monthSignVo.setYearMonth(LocalDateTimeUtils.toStrYearMonth(time.getYear(), time.getMonthValue()));

            // 查询当月签约计划供货计划金额
            SignProductDetailBO monthSupplyPlanBo = getSupplyPlanDetail(productSupplyList, time, isParking,0);
            // 查询当月签约计划供货实际金额
            SignProductDetailBO monthSupplyActualBo = getSupplyActualDetail(productSupplyList, time, isParking);
            // 查询当月签约计划详情
            SignPlanVersionDetail monthDetail = getSignPlanDetailByTime(productSignList, time);

            if(monthDetail == null){
                monthSignVo.initialize();
                monthSignVoList.add(monthSignVo);
                continue;
            }

            ////////////////////////////////////////// 月初存货///////////////////////////////////////////////////
            //<月初存货>套数/车位数
            BigDecimal earlyNum = monthDetail.getEarlyRoomCount();
            // <月初存货>面积
            BigDecimal earlyArea = monthDetail.getEarlyRoomArea();
            //<月初存货>均价
            BigDecimal avgEarlyPrice = monthDetail.getEarlyAvgPrice();
            //<月初存货>金额
            BigDecimal earlyPrice = monthDetail.getEarlySumPrice();
            //<月初存货>签约计划 套数/车位数
            BigDecimal earlySignPlanNum = monthDetail.getEarlySignRoomCount();
            //<月初存货>签约计划面积
            BigDecimal earlySignPlanArea = monthDetail.getEarlySignRoomArea();
            // <月初存货>签约计划均价
            BigDecimal earlyAvgSignPlanPrice = monthDetail.getEarlySignAvgPrice();
            //<月初存货>签约计划金额
            BigDecimal earlySignPlanPrice = monthDetail.getEarlySignSumPrice();
            //<月初存货>签约实际 套数/车位数
            Integer earlySignActualNum = monthDetail.getEarlySignRoomCountActual();
            //<月初存货>签约实际面积
            BigDecimal earlySignActualArea = monthDetail.getEarlySignRoomAreaActual();
            //<月初存货>签约实际均价
            BigDecimal earlyAvgSignActualPrice = monthDetail.getEarlySignAvgPriceActual();
            //<月初存货>签约实际金额
            BigDecimal earlySignActualPrice = monthDetail.getEarlySignSumPriceActual();
            //<月初存货>签约完成率 = <月初存货>签约计划金额 / <月初存货>签约实际金额 * 100%
            String earlySignFillRate = BigDecimalUtils.calculatedPercentage(earlySignPlanPrice, earlySignActualPrice);
            //<月初存货>实际去化率 = <月初存货>签约实际金额 / <月初存货>金额 * 100%
            String earlyActualRemovalRate = BigDecimalUtils.calculatedPercentage(earlySignActualPrice, earlyPrice);
            //<月初存货>计划去化率 = <月初存货>签约计划金额 / <月初存货>金额 * 100%
            String earlyPlanRemovalRate = BigDecimalUtils.calculatedPercentage(earlySignPlanPrice, earlyPrice);

            ///////////////////////////////////// 月度新供///////////////////////////////////////////////
            //<月度新供>供货套数/车位数
            BigDecimal supplyNum = monthDetail.getSupplyRoomCount();
            //<月度新供>供货面积
            BigDecimal supplyArea = monthDetail.getSupplyRoomArea();
            //<月度新供>供货均价
            BigDecimal supplyAvgPrice = monthDetail.getSupplyAvgPrice();
            //<月度新供>供货金额
            BigDecimal supplyPrice = monthDetail.getSupplySumPrice();


            //月度新供>供货计划套数/车位数
            Integer supplyPlanNum = monthSupplyPlanBo.getRoomCount();
            //<月度新供>供货计划面积
            BigDecimal supplyPlanArea = monthSupplyPlanBo.getRoomArea();
            //<月度新供>供货计划均价
            BigDecimal supplyAvgPlanPrice = monthSupplyPlanBo.getAvgPrice();
            //<月度新供>供货计划金额
            BigDecimal supplyPlanPrice = monthSupplyPlanBo.getSumPrice();

            //<月度新供>供货实际套数/车位数
            Integer supplyActualNum = monthSupplyActualBo.getRoomCount();
            //<月度新供>供货实际面积
            BigDecimal supplyActualArea = monthSupplyActualBo.getRoomArea();
            //<月度新供>供货实际均价
            BigDecimal supplyAvgActualPrice = monthSupplyActualBo.getAvgPrice();
            //<月度新供>供货实际金额
            BigDecimal supplyActualPrice = monthSupplyActualBo.getSumPrice();

            //<月度新供>供货完成率 = <月度新供>供货实际金额 / <月度新供>供货计划金额 * 100%
            String supplyFillRate = BigDecimalUtils.calculatedPercentage(supplyActualPrice, supplyPlanPrice);

            //<月度新供>签约计划 套数/车位数
            BigDecimal supplySignPlanNum = monthDetail.getSupplySignRoomCount();
            //<月度新供>签约计划面积
            BigDecimal supplySignPlanArea = monthDetail.getSupplySignRoomArea();
            //<月度新供>签约计划均价
            BigDecimal supplyAvgSignPlanPrice = monthDetail.getSupplySignAvgPrice();
            //<月度新供>签约计划金额
            BigDecimal supplySignPlanPrice = monthDetail.getSupplySignSumPrice();

            //<月度新供>签约实际 套数/车位数
            Integer supplySignActualNum = monthDetail.getSupplySignRoomCountActual();
            //<月度新供>签约实际面积
            BigDecimal supplySignActualArea = monthDetail.getSupplySignRoomAreaActual();
            //<月度新供>签约实际均价
            BigDecimal supplyAvgSignActualPrice = monthDetail.getSupplySignAvgPriceActual();
            //<月度新供>签约实际金额
            BigDecimal supplySignActualPrice = monthDetail.getSupplySignSumPriceActual();

            //<月度新供>签约完成率 = <月度新供>签约实际金额 / <月度新供>签约计划金额 * 100%
            String supplySignFillRate = BigDecimalUtils.calculatedPercentage(supplySignActualPrice, supplySignPlanPrice);
            //<月度新供>实际去化率 = <月度新供>签约实际金额 / <月度新供>供货实际金额 * 100%
            String supplyActualRemovalRate = BigDecimalUtils.calculatedPercentage(supplySignActualPrice, supplyActualPrice);
            //<月度新供>计划去化率 = <月度新供>签约计划金额 / <月度新供>供货计划金额 * 100%
            String supplyPlanRemovalRate = BigDecimalUtils.calculatedPercentage(supplySignPlanPrice, supplyPlanPrice);

            /////////////////////////////////////////////月度综合///////////////////////////////

            //<月度综合>可售 套数/车位数 = <月初存货>套数/车位数 + <月度新供>供货金额
            BigDecimal syntheticalAvailableNum = earlyNum.add(supplyNum);
            //<月度综合>可售面积 = <月初存货>面积 + <月度新供>面积
            BigDecimal syntheticalAvailableArea = earlyArea.add(supplyArea);
            //<月度综合>可售金额 = <月初存货>金额 + <月度新供>金额
            BigDecimal syntheticalAvailablePrice = earlyPrice.add(supplyPrice);
            //月度综合>可售均价 = <月初存货>套数/车位数 + <月度新供>供货金额
            BigDecimal syntheticalAvailableAvgPrice;
            if (isParking == WhetherEnum.YES.getKey()) {
                //月度综合>可售均价 = <月度综合>可售金额 / <月度综合>可售车位数
                syntheticalAvailableAvgPrice = BigDecimalUtils.divBigDecimal(syntheticalAvailablePrice, syntheticalAvailableNum, 4);
            } else {
                //月度综合>可售均价 = <月度综合>可售金额 / <月度综合>可售面积
                syntheticalAvailableAvgPrice = BigDecimalUtils.divBigDecimal(syntheticalAvailablePrice, syntheticalAvailableArea, 4);
            }

            //<月度综合>签约计划 套数/车位数 = <月初存货>签约计划 套数/车位数 + <月度新供>签约计划 套数/车位数
            BigDecimal syntheticalSignPlanNum = earlySignPlanNum.add(supplySignPlanNum);
            //<月度综合>签约计划面积 = <月初存货>签约计划面积 + <月度新供>签约计划面积
            BigDecimal syntheticalSignPlanArea = earlySignPlanArea.add(supplySignPlanArea);
            //<月度综合>签约计划金额 = <月初存货>签约计划金额 + <月度新供>签约计划金额
            BigDecimal syntheticalSignPlanPrice = earlySignPlanPrice.add(supplySignPlanPrice);
            //<月度综合>签约计划均价
            BigDecimal syntheticalAvgSignPlanPrice;
            if (isParking == WhetherEnum.YES.getKey()) {
                //月度综合>签约计划均价 = <月度综合>签约计划金额 / <月度综合>签约计划车位数
                syntheticalAvgSignPlanPrice = BigDecimalUtils.divBigDecimal(syntheticalSignPlanPrice, syntheticalSignPlanNum, 4);
            } else {
                //月度综合>签约计划均价 = <月度综合>签约计划金额 / <月度综合>签约计划面积
                syntheticalAvgSignPlanPrice = BigDecimalUtils.divBigDecimal(syntheticalSignPlanPrice, syntheticalSignPlanArea, 4);
            }

            //<月度综合>签约实际 套数/车位数 = <月初存货>签约实际 套数/车位数 + <月度新供>签约实际 套数/车位数
            Integer syntheticalSignActualNum = earlySignActualNum + supplySignActualNum;
            //<月度综合>签约实际面积 = <月初存货>签约实际面积 + <月度新供>签约实际面积
            BigDecimal syntheticalSignActualArea = earlySignActualArea.add(supplySignActualArea);
            //<月度综合>签约实际金额 = <月初存货>签约实际金额 + <月度新供>签约实际金额
            BigDecimal syntheticalSignActualPrice = earlySignActualPrice.add(supplySignActualPrice);
            //<月度综合>签约实际均价
            BigDecimal syntheticalAvgSignActualPrice;
            if (isParking == WhetherEnum.YES.getKey()) {
                //月度综合>签约实际均价 = <月度综合>签约实际金额 / <月度综合>签约实际车位数
                syntheticalAvgSignActualPrice = BigDecimalUtils.divInteger(syntheticalSignActualPrice, syntheticalSignActualNum, 4);
            } else {
                //月度综合>签约实际均价 = <月度综合>签约实际金额 / <月度综合>签约实际面积
                syntheticalAvgSignActualPrice = BigDecimalUtils.divBigDecimal(syntheticalSignActualPrice, syntheticalSignActualArea, 4);
            }

            //<月度综合>签约完成率 = <月度综合>签约实际金额 / <月度综合>签约计划金额 * 100%
            String syntheticalSignFillRate = BigDecimalUtils.calculatedPercentage(syntheticalSignActualPrice, syntheticalSignPlanPrice);
            //<月度综合>实际去化率 = <月度综合>签约实际金额 / <月度综合>可售金额 * 100%
            String syntheticalActualRemovalRate = BigDecimalUtils.calculatedPercentage(syntheticalSignActualPrice, syntheticalAvailablePrice);
            //<月度综合>计划去化率 = <月度综合>签约计划金额 / <月度综合>可售金额 * 100%
            String syntheticalPlanRemovalRate = BigDecimalUtils.calculatedPercentage(syntheticalSignPlanPrice, syntheticalAvailablePrice);

            monthSignVo.setEarlyNum(String.valueOf(earlyNum));
            monthSignVo.setEarlyArea(String.valueOf(earlyArea));
            monthSignVo.setAvgEarlyPrice(String.valueOf(avgEarlyPrice));
            monthSignVo.setEarlyPrice(String.valueOf(earlyPrice));
            monthSignVo.setEarlySignPlanNum(String.valueOf(earlySignPlanNum));
            monthSignVo.setEarlySignPlanArea(String.valueOf(earlySignPlanArea));
            monthSignVo.setEarlyAvgSignPlanPrice(String.valueOf(earlyAvgSignPlanPrice));
            monthSignVo.setEarlySignPlanPrice(String.valueOf(earlySignPlanPrice));
            monthSignVo.setEarlySignActualNum(String.valueOf(earlySignActualNum));
            monthSignVo.setEarlySignActualArea(String.valueOf(earlySignActualArea));
            monthSignVo.setEarlyAvgSignActualPrice(String.valueOf(earlyAvgSignActualPrice));
            monthSignVo.setEarlySignActualPrice(String.valueOf(earlySignActualPrice));
            monthSignVo.setEarlySignFillRate(earlySignFillRate);
            monthSignVo.setEarlyActualRemovalRate(earlyActualRemovalRate);
            monthSignVo.setEarlyPlanRemovalRate(earlyPlanRemovalRate);
            monthSignVo.setSupplyPlanNum(String.valueOf(supplyPlanNum));
            monthSignVo.setSupplyPlanArea(String.valueOf(supplyPlanArea));
            monthSignVo.setSupplyAvgPlanPrice(String.valueOf(supplyAvgPlanPrice));
            monthSignVo.setSupplyPlanPrice(String.valueOf(supplyPlanPrice));
            monthSignVo.setSupplyActualNum(String.valueOf(supplyActualNum));
            monthSignVo.setSupplyActualArea(String.valueOf(supplyActualArea));
            monthSignVo.setSupplyAvgActualPrice(String.valueOf(supplyAvgActualPrice));
            monthSignVo.setSupplyActualPrice(String.valueOf(supplyActualPrice));
            monthSignVo.setSupplyFillRate(supplyFillRate);
            monthSignVo.setSupplySignPlanNum(String.valueOf(supplySignPlanNum));
            monthSignVo.setSupplySignPlanArea(String.valueOf(supplySignPlanArea));
            monthSignVo.setSupplyAvgSignPlanPrice(String.valueOf(supplyAvgSignPlanPrice));
            monthSignVo.setSupplySignPlanPrice(String.valueOf(supplySignPlanPrice));
            monthSignVo.setSupplySignActualNum(String.valueOf(supplySignActualNum));
            monthSignVo.setSupplySignActualArea(String.valueOf(supplySignActualArea));
            monthSignVo.setSupplyAvgSignActualPrice(String.valueOf(supplyAvgSignActualPrice));
            monthSignVo.setSupplySignActualPrice(String.valueOf(supplySignActualPrice));
            monthSignVo.setSupplySignFillRate(supplySignFillRate);
            monthSignVo.setSupplyActualRemovalRate(supplyActualRemovalRate);
            monthSignVo.setSupplyPlanRemovalRate(supplyPlanRemovalRate);
            monthSignVo.setSyntheticalAvailableNum(String.valueOf(syntheticalAvailableNum));
            monthSignVo.setSyntheticalAvailableArea(String.valueOf(syntheticalAvailableArea));
            monthSignVo.setSyntheticalAvailableAvgPrice(String.valueOf(syntheticalAvailableAvgPrice));
            monthSignVo.setSyntheticalAvailablePrice(String.valueOf(syntheticalAvailablePrice));
            monthSignVo.setSyntheticalSignPlanNum(String.valueOf(syntheticalSignPlanNum));
            monthSignVo.setSyntheticalSignPlanArea(String.valueOf(syntheticalSignPlanArea));
            monthSignVo.setSyntheticalAvgSignPlanPrice(String.valueOf(syntheticalAvgSignPlanPrice));
            monthSignVo.setSyntheticalSignPlanPrice(String.valueOf(syntheticalSignPlanPrice));
            monthSignVo.setSyntheticalSignActualNum(String.valueOf(syntheticalSignActualNum));
            monthSignVo.setSyntheticalSignActualArea(String.valueOf(syntheticalSignActualArea));
            monthSignVo.setSyntheticalAvgSignActualPrice(String.valueOf(syntheticalAvgSignActualPrice));
            monthSignVo.setSyntheticalSignActualPrice(String.valueOf(syntheticalSignActualPrice));
            monthSignVo.setSyntheticalSignFillRate(syntheticalSignFillRate);
            monthSignVo.setSyntheticalActualRemovalRate(syntheticalActualRemovalRate);
            monthSignVo.setSyntheticalPlanRemovalRate(syntheticalPlanRemovalRate);

            monthSignVoList.add(monthSignVo);
        }
        return monthSignVoList;
    }

    /**
     * 计算全盘供货与签约
     * @param productSupply 产品构成供货详情
     * @param productSignList 产品构成签约详情列表
     * @param isParking 是否车位
     * @return
     */
    private SupplyAndSignVo getSupplyAndSignVo(SignPlanVersionSupplyDetail productSupply, List<SignPlanVersionDetail> productSignList, Integer isParking) {
        SupplyAndSignVo vo = new SupplyAndSignVo();

        //<全盘可售货值>套数/车位数
        Integer availableNum;
        //<全盘可售货值>面积
        BigDecimal availableArea = BigDecimal.ZERO;
        //<全盘可售货值>均价
        BigDecimal availableAvgPrice;
        //<全盘可售货值>金额
        BigDecimal availablePrice;
        //<累计已供>套数/车位数
        Integer supplyActualNum;
        //<累计已供>面积
        BigDecimal supplyActualArea = BigDecimal.ZERO;
        //<累计已供>均价
        BigDecimal supplyActualAvgPrice;
        //<累计已供>金额
        BigDecimal supplyActualPrice;
        if(isParking ==WhetherEnum.YES.getKey()){
            availableNum = productSupply.getParkingNum();
            availablePrice = productSupply.getNotCommodityValue();
            availableAvgPrice = BigDecimalUtils.divInteger(availablePrice,availableNum,4);
            supplyActualNum = productSupply.getParkingNumActual();
            supplyActualPrice = productSupply.getNotCommodityValueActual();
            supplyActualAvgPrice = BigDecimalUtils.divInteger(supplyActualPrice,supplyActualNum,4);
        }else {
            availableNum = productSupply.getRoomNum();
            availableArea = productSupply.getSaleArea();
            availablePrice = productSupply.getHouseCommodityValue();
            availableAvgPrice = BigDecimalUtils.divBigDecimal(availablePrice,availableArea,4);
            supplyActualNum = productSupply.getRoomNumActual();
            supplyActualArea = productSupply.getAreaActual();
            supplyActualPrice = productSupply.getHouseCommodityValueActual();
            supplyActualAvgPrice = BigDecimalUtils.divBigDecimal(supplyActualPrice,supplyActualArea,4);
        }

        SignProductDetailBO signActualBo = getSignActualPrice(productSignList,isParking);

        //<累计已售>套数/车位数
        Integer signActualNum = signActualBo.getRoomCount();
        //<累计已售>面积
        BigDecimal signActualArea = signActualBo.getRoomArea();
        //<累计已售>均价
        BigDecimal signAvgActualPrice = signActualBo.getAvgPrice();
        //<累计已售>金额
        BigDecimal signActualPrice = signActualBo.getSumPrice();

        //<供货完成率>% = <累计已供>金额 /<全盘可售货值>金额
        String supplyFillRate = BigDecimalUtils.calculatedPercentage(supplyActualPrice, availablePrice);
        //<签约完成率>% = <累计已售>金额 / <全盘可售货值>金额
        String signFillRate = BigDecimalUtils.calculatedPercentage(signActualPrice, availablePrice);
        //<实际去化率>% = <累计已售>金额 / <累计已供>金额
        String actualRemovalRate = BigDecimalUtils.calculatedPercentage(signActualPrice, supplyActualPrice);


        vo.setAvailableNum(String.valueOf(availableNum));
        vo.setAvailableArea(String.valueOf(availableArea));
        vo.setAvailableAvgPrice(String.valueOf(availableAvgPrice));
        vo.setAvailablePrice(String.valueOf(availablePrice));
        vo.setSupplyActualNum(String.valueOf(supplyActualNum));
        vo.setSupplyActualArea(String.valueOf(supplyActualArea));
        vo.setSupplyActualAvgPrice(String.valueOf(supplyActualAvgPrice));
        vo.setSupplyActualPrice(String.valueOf(supplyActualPrice));
        vo.setSignActualNum(String.valueOf(signActualNum));
        vo.setSignActualArea(String.valueOf(signActualArea));
        vo.setSignAvgActualPrice(String.valueOf(signAvgActualPrice));
        vo.setSignActualPrice(String.valueOf(signActualPrice));
        vo.setSupplyFillRate(supplyFillRate);
        vo.setSignFillRate(signFillRate);
        vo.setActualRemovalRate(actualRemovalRate);
        return vo;
    }

    /**
     * 处理审批单查看内容缓存
     * @param businesskey 审批单id
     * @param approvedState 审批单状态
     */
    @Override
    public void handApprovalBillBuffer(String businesskey, String approvedState){

        //审批中状态 对审批单查看内容做缓存
        if(ApprovedStateCodeEnum.APPROVAL.getKey().equals(approvedState)){
            SigPlanPreviewVo vo = getApprovalPreview(null, businesskey);
            redisTemplateUtil.set(businesskey,vo,60*60*24*365L);
            saveLog(LogEventEnum.REFRESH,"签约计划","处理审批单查看内容缓存","对审批单查看内容做缓存,审批单id:"+businesskey+",审批单状态:"+approvedState);
        //驳回发起状态 将审批单查看内容缓存删除
        }else if(ApprovedStateCodeEnum.REJECT.getKey().equals(approvedState)){
            redisTemplateUtil.delete(businesskey);
            saveLog(LogEventEnum.REFRESH,"签约计划","处理审批单查看内容缓存","将审批单查看内容缓存删除,审批单id:"+businesskey+",审批单状态:"+approvedState);
            //撤回发起状态 将审批单查看内容缓存删除
        }else if(ApprovedStateCodeEnum.RECALL.getKey().equals(approvedState)){
            redisTemplateUtil.delete(businesskey);
            saveLog(LogEventEnum.REFRESH,"签约计划","处理审批单查看内容缓存","将审批单查看内容缓存删除,审批单id:"+businesskey+",审批单状态:"+approvedState);
            //废弃流程状态 将审批单查看内容缓存删除
        }else if(ApprovedStateCodeEnum.ENDFLOW.getKey().equals(approvedState)){
            redisTemplateUtil.delete(businesskey);
            saveLog(LogEventEnum.REFRESH,"签约计划","处理审批单查看内容缓存","将审批单查看内容缓存删除,审批单id:"+businesskey+",审批单状态:"+approvedState);
        }
    }

    @Autowired
    ReturnPlanVersionService returnPlanVersionService;

    /**
     * 接审批消息后会操作
     * @param versionId 版本id
     * @param state 版本状态
     * @param eventType 审批事件类型
     */
    public void  successSpproved(String versionId ,String state,Integer eventType){
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
        if(null == signPlanVersion){
            return;
        }
        //修改签约计划版本状态
        signPlanVersionMapper.updateSignPlanVersionState(state,versionId,LocalDateTime.now());
        saveVersionLog(versionId,VersionEventEnum.toVersionEvent(eventType),signPlanVersion.getVersionState(),Constants.SYS,"接OA审批消息",null);

        if(state.equals(VersionStatusEnum.SUCCESSAPPROVED.getKey())){
            //更新分期最新版本
            updateStageNewest(versionId);

            try {
                //发送回款计划代办
                returnPlanVersionService.returnNoticeDispose(versionId);
            }catch (Exception e){
                log.error(e.getMessage());
                SysLogUtils.addSysLog(LogEventEnum.ADD,"回款计划","自动创建回款计划失败",e.getMessage());
            }

        }
    }

    /**
	 * 更新分期最新版本（当有版本审批通过时会被调用）
	 * @param versionId
	 */
	public void updateStageNewest(String versionId){
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(versionId, IsDeleteEnum.NO.getKey());
        if(null == signPlanVersion){
            return;
        }
        //将分期下所有版本 是否分期最新标记修改为0
        Integer isStageNewest=WhetherEnum.NO.getKey();
        signPlanVersionMapper.updateIsStageNewestByProjectfId(signPlanVersion.getProjectfId(),isStageNewest);
        //将最新版本 是否分期最新标记修改为1
        signPlanVersion.setIsStageNewest(WhetherEnum.YES.getKey());
        signPlanVersionMapper.updateById(signPlanVersion);
	}

    /**
     * 获取去年的计划签约的条数
     * @return
     */
    public int getYearSignPlanNum(List<String> versionIds, String productCode, String year,String businessTypeName
            ,String freeTypeName,String proTypeName) {
        String lastYear = Integer.parseInt(year) - 1 + "";
        List<MonthAvgSaleRateDeviationDto> list = signPlanVersionDetailMapper.selectYearSignPlanInfo(versionIds,
                IsDeleteEnum.NO.getKey(), lastYear, productCode,businessTypeName,freeTypeName,proTypeName);
        int num = 0;
        if (list.size() > 0) {
            for (MonthAvgSaleRateDeviationDto x : list) {
                double amount = Double.parseDouble(x.getQyPlanAmount());
                if (amount > 0) {
                    num++;
                }
            }
        }
        return num;
    }
    /**
     * 签约代办处理
     * @param versionId 供货版本id
     */
    public void signNoticeDispose(String versionId) {
        //供货版本
        SupplyPlanVersion supplyVersion = supplyPlanVersionMapper.selectById(versionId);
        if(null == supplyVersion){
            saveLog(LogEventEnum.SUBMISSION,"签约计划","签约计划代办失败","供货生成新的版本为空,版本id:"+versionId);
            throw new MsgException(CodeEnum.SUPPLY_PLAN_VERSION_ERROR);
        }

        String projectId = supplyVersion.getProjectId();
        String stageId = supplyVersion.getProjectfId();
        String versionType = supplyVersion.getVersionType();


        IdmUser handUser = new IdmUser();
        handUser.setAlias(Constants.SYS);
        handUser.setUsercn("系统");
        //查询是否已有代办，将原代办消除
        deleteTodoInfo(stageId + versionType,OperatorTypeEnum.DELETE.getKey(),handUser);

        if(!versionType.equals(VersionTypeEunm.INVESTMENT.getKey()) && !versionType.equals(VersionTypeEunm.WARPLAN.getKey())){
            //不是投资版或战规版，不发送代办
            return;
        }
        ProjectByStagesDto stage = projectByStagesMapper.selectByOrgdepid(stageId);

        List<ProjectByStages> parentList = projectMapper.selectParentData(projectId);
        String businessId="";
        String cityId="";
        String pcUrl = "";
        String projectName="";
        if(CollectionUtils.isNotEmpty(parentList)) {
            for(ProjectByStages obj : parentList) {
                String typeId = obj.getHrOrgTypeId();
                if(typeId.equals(HrOrgTypeEunm.ORG_21.getKey())) {
                    cityId = obj.getOrgdepid();
                }else if(typeId.equals(HrOrgTypeEunm.ORG_20.getKey())) {
                    businessId = obj.getOrgdepid();
                }else {
                    if(obj.getOrgdepid().equals(projectId)) {
                        projectName = obj.getOrgname();
                    }
                }
            }
        }

        String assessmentName = "";
        if(supplyVersion.getIsAssessmentVersion() != null && supplyVersion.getIsAssessmentVersion() == WhetherEnum.YES.getKey()){
            assessmentName="(考核版)";
        }
        String versionTypeName = VersionTypeEunm.getValueByKey(versionType);
        String title = projectName+stage.getOrgname()+"有了新版"+versionTypeName+"供货计划:"+ versionTypeName + supplyVersion.getVersionNum()
                +"-" + LocalDateTimeUtils.formatTime(supplyVersion.getCreateTime(), "yyyyMMdd") + assessmentName + ",请编排该分期" + versionTypeName + "签约计划";
        pcUrl = signNoticePcUrl+businessId+"/"+cityId+"/"+projectId+"/"+stageId+"/"+versionType;

        //        https://mars-uat.cifi.com.cn/common/app/todoNotice?taskId=xxxxx
        String mobileTaskUrl = "";
        List<IdmUser> userList = idmUserService.getPositionGroupUserList(Constants.PROJECT_MARKETING_PM,projectId);
        PortalTodoBo doorTodoBo = new PortalTodoBo();
        //移动端地址，钉钉上收到待办时，点击后进入的地址。（可为钉钉应用首页）必传
        doorTodoBo.setMobileTaskUrl(signNoticeAppUrl);
        //PC端地址，门户portal.cifi.com.cn中点击一条待办后进入的地址。必传
        doorTodoBo.setPcTaskUrl(pcUrl);
        //待办的主题 自己定义
        doorTodoBo.setTitle(title);
        //任务类型，待办列表中显示在主题前面的中括号中，表示提醒审批人待审批的是什么。如“转交”，“驳回”，“合同审批”等，最多用四个字来总结
        doorTodoBo.setTodoType("编排"+ versionTypeName +"签约计划");
        //任务类型code
        doorTodoBo.setTodoTypeCode(TodoTypeCodeEnum.SIGN_PLAN.getKey());
        // 任务关联id [货值\供货\签约版本id]
        doorTodoBo.setVersionId(stageId+versionType);
        //任务关联id类型
        doorTodoBo.setVersionIdType(VersionIdTypeEnum.SUPPLY.getKey());
        //待办说明
        doorTodoBo.setContent(title);
        //待办组织类型 1 旭辉集团 2 区域或事业部 3 城市公司 4 项目 5分期
        doorTodoBo.setOrgType("5");
        doorTodoBo.setGroupId("00000001");
        doorTodoBo.setRegionId(businessId);
        doorTodoBo.setCityId(cityId);
        doorTodoBo.setProjectId(projectId);
        doorTodoBo.setStagesId(stageId);

        saveLog(LogEventEnum.SUBMISSION,"签约计划","签约计划发送代办",doorTodoBo.toString()+"/"+userList.toString());
        String num = commonService.start("", doorTodoBo, userList);
    }

    @Autowired
    private PortalTodoTaskMapper portalTodoTaskMapper;

    /**
     * 消除代办
     * @param versionId 代办关联id
     * @param operatorType 处理方式
     * @param user 处理人
     */
    public String deleteTodoInfo(String versionId,String operatorType,IdmUser handUser) {
        //查询代办
        PortalTodo portalTodo = portalTodoMapper.selectByVersionIdAndTodoTypeCode(versionId,TodoTypeCodeEnum.SIGN_PLAN.getKey());
        if(null != portalTodo){
            String instanceId = portalTodo.getInstanceId();
            List<PortalTodoTask> list = portalTodoTaskMapper.selectByInstanceId(portalTodo.getInstanceId());
            if(CollectionUtils.isNotEmpty(list)) {
                for(PortalTodoTask todoTask : list) {
                    String taskApproveId = todoTask.getTaskApproveId();
                    IdmUser user = idmUserService.getByAlias(taskApproveId);

                    saveLog(LogEventEnum.EDIT,"签约计划","签约计划消除代办","代办id:"+instanceId);
                    commonService.updatePortalProcess(instanceId, operatorType, handUser, user);
                }
            }else {
                saveLog(LogEventEnum.EDIT,"签约计划","签约计划消除代办","代办id:"+instanceId);
                commonService.updatePortalProcess(instanceId, operatorType, handUser, null);
            }
            return "消除成功";
        }
        return "未查询到此代办消息";
    }


    /**
     * 查询签约计划全部项目
     * @return
     */
    @Override
    public Set<String> selectProjectIdAll() {
        return signPlanVersionMapper.selectProjectIdAll();
    }


    /**
     * 分期是否进入一房一价
     * @param projectfId
     * @return
     */
    private Integer isContainSale(String projectfId){
        List<MymRooms> rooms = mymRoomsService.selectNewestWarPlanByFid(projectfId);
        if(CollectionUtils.isNotEmpty(rooms)){
            return 1;
        }
        return 0;
    }


    /**
     * 版本日志
     * @param versionId 版本id
     * @param event 版本事件
     * @param beforeState 变更前的状态
     * @param alias 创建人
     * @param title 事件标题
     * @param content 事件内容
     */
    @Override
    public void saveVersionLog(String versionId, String event, String beforeState, String alias, String title, String content){
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectById(versionId);

        String versionName = VersionUtils.getFullName(signPlanVersion);
        VersionLog versionLog = new VersionLog();
        versionLog.setId(UUIDUtils.create());
        versionLog.setProjectId(signPlanVersion.getProjectId());
        versionLog.setProjectfId(signPlanVersion.getProjectfId());
        versionLog.setModuleType(ModuleTypeEunm.SIGN_PLAN.getKey());
        versionLog.setEvent(event);
        versionLog.setVersionId(versionId);
        versionLog.setVersionName(versionName);
        versionLog.setBeforeState(beforeState);
        versionLog.setAfterState(signPlanVersion.getVersionState());
        versionLog.setModifySourceType(null);
        versionLog.setModifySourceId(null);
        versionLog.setOperationBy(alias);
        versionLog.setOperationTime(LocalDateTime.now());
        versionLog.setTitle(title);
        versionLog.setContent(content);
        versionLog.setIsDelete(0);
        versionLog.setCreateBy(alias);
        versionLog.setCreateTime(LocalDateTime.now());

        versionLogMapper.insert(versionLog);
    }

    /**
     * 更新版本状态
     * @param versionIds
     * @param versionState
     * @param user
     */
    @Override
    public void updateState(List<String> versionIds, String versionState, IdmUser user) {
        //参数校验
        if(CollectionUtils.isEmpty(versionIds) || StringUtils.isBlank(versionState) || null == user){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        List<SignPlanVersion> signPlanVersionList = signPlanVersionMapper.selectByVersionIds(versionIds, IsDeleteEnum.NO.getKey());
        if(CollectionUtils.isEmpty(signPlanVersionList)){
            return;
        }
        signPlanVersionList.forEach(s->{
            s.setVersionState(versionState);
            s.setUpdateBy(user.getAlias());
            s.setUpdateTime(LocalDateTime.now());
        });

        this.updateBatchById(signPlanVersionList);
    }


    /**
     * 判断是否售罄版本
     * @param signPlanVersion
     */
    @Override
    public boolean getIsSoldOutVersion(SignPlanVersion signPlanVersion) {
        //获取版本签约明细列表
        List<SignPlanVersionDetail> signPlanVersionDetailList =signPlanVersionDetailMapper .selectByVersionId(signPlanVersion.getId(),IsDeleteEnum.NO.getKey());
        //获取动态版最新货值版本
        PriceVersion priceVersion  = priceVersionService.getNewestPriceVersion(signPlanVersion.getProjectfId());
        //获取版本货值明细数据
        PriceValueOrg priceValueOrg = priceValueOrgMapper.selectValueOrgById(priceVersion.getId(), OrgTypeEunm.ORG_STAGE.getKey());
        //实际签约金额
        BigDecimal actualSignPrice = signPlanVersionDetailList.stream()
                .filter(s-> !signPlanVersion.getAffiliationMonth().equals(LocalDateTimeUtils.getLocalMonth(s.getSignPlanTime())))
                .map(s -> s.getEarlySignSumPriceActual().add(s.getSupplySignSumPriceActual())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        //已售金额
        BigDecimal soldPrice = priceValueOrg.getPriceSold();
        //总货值金额
        BigDecimal totalPrice = priceValueOrg.getHouseCommodityValue().add(priceValueOrg.getNotCommodityValue());

        if(actualSignPrice.compareTo(soldPrice) == 0 && soldPrice.compareTo(totalPrice) == 0){
            return true;
        }
        return false;
    }


    /**
     * 保存系统日志
     */
    private boolean saveLog(LogEventEnum logEventEnum,String module,String title,String content) {
        SysLogBO bo = new SysLogBO();
        bo.setEvent(logEventEnum);
        bo.setSource(LogSrcEnum.PC);
        String hostAddr =WebUtils.getLocalIP();
        bo.setIp(hostAddr);
        bo.setModule(module);
        bo.setTitle(title);
        bo.setContent(content);
        bo.setType(LogTypeEnum.ZERO.getKey());
        bo.setIsSuccess(IsSuccessEnum.SUCCESS.getKey());
        return sysLogService.add(bo,Constants.SYS);
    }

}
